需要了解Python闭包的帮助吗

需要了解Python闭包的帮助吗,python,closures,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

我有以下代码:

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
回溯(最近一次呼叫最后一次):
文件“C:/Python26/replace.py”,第13行,在
打印数据替换(测试)
文件“C:/Python26/replace.py”,第10行,在doReplace中
返回子章节(“章节[a-zA-Z]+”,章节更换器,测试)
文件“C:\Python26\lib\re.py”,第151行,子文件
返回编译(模式,0).sub(repl,字符串,计数)
ChapterReplace中第6行的文件“C:/Python26/replace.py”
结果='第%i章“%i”
UnboundLocalError:赋值前引用的局部变量“i”

我的印象是chapterReplacer将捕获局部变量I,但这似乎没有发生?

没有,在python 2中,如果不使用可变项技巧,就根本无法实现:

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只会在周围的范围内查找未在本地分配的变量;一旦字节编译器看到一个直接赋值(
i=something
)并且没有
global i
语句来说服它,变量就被认为是局部变量

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

在python 3中,只需使用以下命令,让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)

您可以将
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开始,您可以使用
非本地的
la@MartijnPieters的解决方案。

在python中,如果您在函数中赋值给变量(即使使用了复合赋值运算符,如
+=
),除非
global
nonlocal
语句另有规定,否则该变量被视为局部变量。

当编译器看到该变量
i
在函数
chapterReplacer
中获取另一个值时,它将其视为局部变量,并且不应用“闭包魔法”。如果删除行
i+=1
,代码将运行。

相关:dupe目标在这里没有多大帮助,因为这是关于试图从父作用域分配名称的嵌套函数。如果仅在doReplace中调用ChapterReplace,这是否重要?@Hans:是,因为每次运行
doReplace
都可以得到一个新的
chapterReplacer
。如果将属性置于
doReplace
,然后再次运行
doReplace
,则将使用相同的
i
,该属性在newacct时不正确,但每次进入doReplace时都显式重置该值,那么这不重要吗?@Hans:如果你有多个线程,或者里面的代码可以递归调用
doReplace
或者类似的东西,那么这仍然很重要。正是这样的微妙之处导致很难跟踪bug,为什么我不使用函数属性来处理闭包破坏变量呢!因为您没有重新分配
i
;仅限
i
的成员。变量本身是可变的,但由于它的直接值(列表)不变,python将查看周围的范围。