需要了解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将查看周围的范围。