需要帮助理解Python闭包

时间:2012-06-15 19:59:42

标签: python closures

我有这段代码:

import re

def doReplace(toReplace):
    i = 1
    def chapterReplacer(_):
        result = 'Chapter %i' % i
        i += 1
        return result

    return re.sub('Chapter [a-zA-Z]+', chapterReplacer, test)

test = 'Chapter one Chapter Two Chapter three'
print doReplace(test)

当我运行它时,我收到以下错误:

Traceback (most recent call last):
  File "C:/Python26/replace.py", line 13, in <module>
    print doReplace(test)
  File "C:/Python26/replace.py", line 10, in doReplace
    return re.sub('Chapter [a-zA-Z]+', chapterReplacer, test)
  File "C:\Python26\lib\re.py", line 151, in sub
    return _compile(pattern, 0).sub(repl, string, count)
  File "C:/Python26/replace.py", line 6, in chapterReplacer
    result = 'Chapter %i' % i
UnboundLocalError: local variable 'i' referenced before assignment

我的印象是chapterReplacer会捕获局部变量i,但这似乎不会发生?

4 个答案:

答案 0 :(得分:6)

不,并且在python 2中你根本不能使用带有mutable的技巧:

def doReplace(toReplace):
    i = [1]
    def chapterReplacer(_):
        result = 'Chapter %i' % i[0]
        i[0] += 1
        return result

    return re.sub('Chapter [a-zA-Z]+', chapterReplacer, test)

通常情况下,如果变量未被分配到本地,python将只查看变量的周围范围;一旦bytecompiler看到直接赋值(i = something)而没有global i语句来说服它,否则变量被认为是本地的。

但在上面的代码中,我们从未在i函数中分配给chapterReplacer。是的,我们确实更改了i[0],但i本身(列表)中存储的值不会更改。

在python 3中,只需使用nonlocal statement让python查看变量的闭包:

def doReplace(toReplace):
    i = 1
    def chapterReplacer(_):
        nonlocal i
        result = 'Chapter %i' % i
        i += 1
        return result

    return re.sub('Chapter [a-zA-Z]+', chapterReplacer, test)

答案 1 :(得分:2)

您可以将i设为功能属性

def doReplace(toReplace):
    chapterReplacer.i = 1
    def chapterReplacer(_):
        result = 'Chapter %i' % chapterReplacer.i
        chapterReplacer.i += 1
        return result

    return re.sub('Chapter [a-zA-Z]+', chapterReplacer, test)

编辑:从python 3开始,您可以使用nonlocal一个@MartijnPieters的解决方案。

答案 2 :(得分:2)

在Python中,如果在函数内部分配变量(即使使用复合赋值运算符,例如+=),除非global或{{1}另有指定,否则该变量将被视为本地变量声明。

答案 3 :(得分:1)

当编译器看到变量i在函数chapterReplacer中获得另一个值时,它将其视为本地,并且不应用“闭合魔术”。如果您删除行i += 1,您的代码将会运行。