mad*_*erd 5 python closures scope common-lisp local-variables
我正在阅读黑客和画家,并对作者提到的问题感到困惑,以说明不同编程语言的力量.
问题是:
我们想编写一个生成累加器的函数 - 一个取n的函数,并返回一个函数,该函数接受另一个数字i并返回n递增i.(这是增加的,而不是加号.累加器必须累积.)
作者提到了几种使用不同编程语言的解决方案.例如,Common Lisp:
(defun foo (n)
(lambda (i) (incf n i)))
Run Code Online (Sandbox Code Playgroud)
和JavaScript:
function foo(n) { return function (i) { return n += i } }
Run Code Online (Sandbox Code Playgroud)
但是,当涉及到Python时,以下代码不起作用:
def foo(n):
s = n
def bar(i):
s += i
return s
return bar
f = foo(0)
f(1) # UnboundLocalError: local variable 's' referenced before assignment
Run Code Online (Sandbox Code Playgroud)
一个简单的修改将使其工作:
def foo(n):
s = [n]
def bar(i):
s[0] += i
return s[0]
return bar
Run Code Online (Sandbox Code Playgroud)
我是Python的新手.为什么第一个解决方案不起作用而第二个解决方案不起作用?作者提到了词汇变量,但我仍然没有得到它.
s += i只是糖s = s + i.*
这意味着您为变量分配一个新值s(而不是在适当的位置进行变更).分配给变量时,Python假定它是函数的本地变量.但是,在分配之前需要进行评估s + i,但是s是本地的仍然未分配 - >错误.
在第二种情况下,s[0] += i您永远不会s直接分配,但只能从中访问项目s.所以Python可以清楚地看到它不是局部变量,而是在外部范围内寻找它.
最后,一个更好的替代方法(在Python 3中)是明确地告诉它s不是局部变量:
def foo(n):
s = n
def bar(i):
nonlocal s
s += i
return s
return bar
Run Code Online (Sandbox Code Playgroud)
(实际上没有必要s- 你可以简单地n在内部使用bar.)
*情况略微复杂,但重要的问题是计算和分配是在两个单独的步骤中执行的.