在 Python 的闭包中为什么不能改变被捕获变量?

2014-12-25 18:00:07 +08:00
 eriale
def fn(n):
    def _inner(i):
        n += i
        return n
    return _inner

返回的错误是:

UnboundLocalError: local variable 'n' referenced before assignment

我看了SO上的提问这篇文章, 在python2用一个容器包裹n后可以修改,如果在Python3可以通过声明nonlocal来解决这个问题。

因为JS里面可以直接对n修改,我想问的是Python对闭包中变量的处理跟Javascript中有什么不同?

3784 次点击
所在节点    问与答
14 条回复
rcmerci
2014-12-25 18:13:38 +08:00
python2里面就是这样的。
好像python3改了(没用过3不确定)
所以我更喜欢js
clino
2014-12-25 19:06:49 +08:00
问题出在 n += i 上,关键是有赋值,python 认为这里声明了一个局部变量
换个变量名就行
clino
2014-12-25 19:07:59 +08:00
def _inner(i):
j = n + i
return j
yjfuk
2014-12-25 19:08:41 +08:00
ffffwh
2014-12-25 19:12:25 +08:00
闭包也是函数式编程推广开来的概念,函数式编程一般要无副作用。在有副作用的环境下闭包该是个什么套路我是没想清楚的。
tombkeeper
2014-12-25 19:29:19 +08:00
use nonlocal
sujin190
2014-12-25 20:23:22 +08:00
里边用global n声明就行了
sujin190
2014-12-25 20:28:03 +08:00
好吧,还没注意过这样不行,直觉按python的设计应该是可以的,原来不行啊
clino
2014-12-25 21:55:54 +08:00
@sujin190 你有试过global可以吗?
sujin190
2014-12-25 22:42:02 +08:00
@clino 试过了,不行
bottleimp
2014-12-26 09:58:34 +08:00
lz 你要用闭包, 你就得有 immutable 的思想.
eriale
2014-12-26 10:53:37 +08:00
@yjfuk 原来的代码是要在n上累加,你的代码是不能累加的。
eriale
2014-12-26 10:57:32 +08:00
@bottleimp 闭包不一定要immutable吧,这个例子是在<黑客和画家>看到的,Ruby/JS是可以直接修改被捕获的变量,我就想知道Python对闭包中变量的处理有哪些不一样?更进一步,Python这么设计有什么考虑吗?
bottleimp
2014-12-26 17:38:33 +08:00
@eriale 我对 ruby 还有 JS 不熟, 单纯的从 python 的角度看, 我觉得这个是跟语言具体实现时候有关的.
一个函数, 背后实际应该是由一个环境(env) 跟一个函数来组成的. python 中对这个 env 里的变量, 还会区分是否 local. 当函数里有赋值的变量, 可以认为打上了 local 标记, 并且 shadow 了env 中原有的同名变量.
所以在使用 n = n + 1 这种语句的时候, 一旦 python 需要计算表达式 n+1 的值, 它就会认为这个 n 是 local varible, 并且还没有 bind, 就会抛 UnboundLocalError.

同样的情况你在最外层的函数也会有, 只是最外层函数我们可以用 global 来限定, 但是你这个是闭包, 相当于中间那层有个变量 n 要在最里层用, 对于这种情况, python2 里面就没有类似 global 这样的对应方法, 在 python3中加了 nonlocal 来处理这种情况.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/156676

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX