这段Python不起作用:
def make_incrementer(start):
def closure():
# I know I could write 'x = start' and use x - that's not my point though (:
while True:
yield start
start += 1
return closure
x = make_incrementer(100)
iter = x()
print iter.next() # Exception: UnboundLocalError: local variable 'start' referenced before assignment
我知道如何解决这个错误,但请耐心等待:
此代码可以正常工作:
def test(start):
def closure():
return start
return closure
x = test(999)
print x() # prints 999
为什么我可以在闭包内读取start
变量但不能写入?
导致start
变量处理的语言规则是什么?
更新:我发现此帖子相关(答案不仅仅是问题):Read/Write Python Closures
答案 0 :(得分:30)
每当在函数内部分配变量时,它将是该函数的局部变量。行start += 1
正在为start
分配新值,因此start
是一个局部变量。由于存在局部变量start
,当您第一次尝试访问它时,该函数不会尝试查看start
的全局范围,因此您会看到错误。
在3.x中,如果您使用nonlocal
关键字,则代码示例将有效:
def make_incrementer(start):
def closure():
nonlocal start
while True:
yield start
start += 1
return closure
在2.x上,您可以使用global
关键字解决类似问题,但这不起作用,因为start
不是全局变量。
在这种情况下,您可以执行类似于您的建议(x = start
)的操作,或者使用可修改的变量来修改并生成内部值。
def make_incrementer(start):
start = [start]
def closure():
while True:
yield start[0]
start[0] += 1
return closure
答案 1 :(得分:9)
在Python 2.x上有两种“更好”/更多Pythonic方法,而不是使用容器来解决缺少非本地关键字的问题。
您在代码中的注释中提到的一个 - 绑定到局部变量。还有另一种方法:
def make_incrementer(start):
def closure(start = start):
while True:
yield start
start += 1
return closure
x = make_incrementer(100)
iter = x()
print iter.next()
这具有局部变量的所有好处,而无需额外的代码行。它也发生在x = make_incrememter(100)
行,而不是iter = x()
行,根据具体情况,这可能或不重要。
您还可以使用“不实际分配给引用变量”方法,以比使用容器更优雅的方式:
def make_incrementer(start):
def closure():
# You can still do x = closure.start if you want to rebind to local scope
while True:
yield closure.start
closure.start += 1
closure.start = start
return closure
x = make_incrementer(100)
iter = x()
print iter.next()
这适用于所有最新版本的Python,并利用这样一个事实:在这种情况下,您已经有一个对象,您知道您可以引用属性的名称 - 没有必要为此目的创建一个新容器
答案 2 :(得分:4)
def make_incrementer(start):
def closure():
# I know I could write 'x = start' and use x - that's not my point though (:
while True:
yield start[0]
start[0] += 1
return closure
x = make_incrementer([100])
iter = x()
print iter.next()
答案 3 :(得分:3)
在Python 3.x中,您可以使用nonlocal
关键字来重新绑定不在本地范围内的名称。在2.x中,您唯一的选择是修改(或变异)闭包变量,将实例变量添加到内部函数,或者(因为您不想这样做)创建一个局部变量......
# modifying --> call like x = make_incrementer([100])
def make_incrementer(start):
def closure():
# I know I could write 'x = start' and use x - that's not my point though (:
while True:
yield start[0]
start[0] += 1
return closure
# adding instance variables --> call like x = make_incrementer(100)
def make_incrementer(start):
def closure():
while True:
yield closure.start
closure.start += 1
closure.start = start
return closure
# creating local variable --> call like x = make_incrementer(100)
def make_incrementer(start):
def closure(start=start):
while True:
yield start
start += 1
return closure