为什么这个闭包不修改封闭范围中的变量?

这一点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变量的处理?

更新 :我发现这个帖子相关(答案不仅仅是问题):读/写Python闭包


无论何时在函数内部分配一个变量,它都将是该函数的局部变量。 行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

在使用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,并利用这样一个事实:在这种情况下,您已经拥有一个对象,您知道您可以引用属性的名称 - 不需要为此创建新的容器。


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()
链接地址: http://www.djcxy.com/p/51247.html

上一篇: Why doesn't this closure modify the variable in the enclosing scope?

下一篇: What is a race condition?