Python閉包的改進
2009年01月4日 原創, 編程技術
Python也支持閉包,但在Python3.0以前,閉包不能訪問外部函數中的局部變量。Python3.0爲此引入了nonlocal關鍵字,從而完善了閉包訪問外部變量的機制。
在Python2.6中,如果像下面這樣定義函數:
1 |
>>> def outerFun():
|
2 |
outerVar = 0
|
3 |
def innerFun():
|
4 |
outerVar + = 1
|
5 |
print outerVar
|
6 |
return innerFun
|
然後,調用outerFun返回的閉包,會導致錯誤:
1 |
>>> f = outerFun()
|
2 |
>>> f() |
3 |
4 |
Traceback (most recent call last): |
5 |
File "<pyshell#15>" , line 1 , in <module>
|
6 |
f()
|
7 |
File "<pyshell#12>" , line 4 , in innerFun
|
8 |
outerVar + = 1
|
9 |
UnboundLocalError: local variable 'outerVar' referenced before assignment
|
把錯誤消息“UnboundLocalError: local variable ‘outerVar’ referenced before assignment”翻譯成中文,就是“未綁定局部變量:引用局部變量’outerVar’之前沒有賦值”。啥意思呢?在內部函數innerFun中,outerVar被Python解釋器看成是內部函數innerFun中的局部變量,並不是我們認爲的外部(函數outerFun中的)變量。即在innerFun中,outerVar只是一個尚未賦值的變量——儘管與外部的outerVar同名,因此不能執行加法計算。Python3.0以前的版本提供了global關鍵字,通過這個關鍵字能夠在函數內部引用並重新綁定(修改)全局變量:
1 |
>>> x = 1
|
2 |
>>> def c():
|
3 |
global x
|
4 |
x + = 1
|
5 |
print x
|
6 |
7 |
>>> c() |
8 |
2 |
這裏通過global關鍵字在函數c的局部作用域中引用了全局變量x,因此就可以重新綁定(修改)全局變量了。雖然能訪問和修改全局變量,但還是不能解決閉包訪問外部函數變量的問題。爲此,Python3.0增加了一個新關鍵字——nonlocal,用於在嵌套函數中訪問外部變量:
01 |
>>> def outerFun():
|
02 |
outerVar = 0
|
03 |
def innerFun():
|
04 |
nonlocal outerVar # 使用nonlocal引用外部函數變量
|
05 |
outerVar + = 1
|
06 |
print (outerVar) # 注意,print在Python3.0中是函數,不是語句
|
07 |
return innerFun
|
08 |
09 |
>>> f = outerFun()
|
10 |
>>> f() |
11 |
1 |
12 |
>>> f() |
13 |
2 |
而且,此時的外部變量對於內部的函數而言是共享的:
01 |
>>> def outerFun():
|
02 |
outerVar = 0
|
03 |
def innerFun():
|
04 |
nonlocal outerVar
|
05 |
outerVar + = 1
|
06 |
print (outerVar)
|
07 |
def innerFun2():
|
08 |
nonlocal outerVar
|
09 |
outerVar * = 2
|
10 |
print (outerVar)
|
11 |
return [innerFun,innerFun2] # 通過列表返回兩個函數
|
12 |
13 |
>>> ff = outerFun()
|
14 |
>>> ff[ 0 ]()
|
15 |
1 |
16 |
>>> ff[ 1 ]()
|
17 |
2 |
18 |
>>> ff[ 1 ]() # 連續調用兩次innerFun2
|
19 |
4 |
20 |
>>> ff[ 0 ]() # 結果表明,兩個閉包共享同一個變量
|
21 |
5 |
關於這一點改進的詳細內容,請參見PEP(Python Enhancement Proposals,Python改進建議)3104:Access to Names in Outer Scopes。