以下這段程式碼是在python中使用閉包時一段經典的錯誤程式碼
阿新 • • 發佈:2018-12-17
def foo():
a = 1
def bar():
a = a + 1
return a
return bar
這段程式的本意是要通過在每次呼叫閉包函式時都對變數a進行遞增的操作。但在實際使用時
>>> c = foo() >>> print c() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in bar UnboundLocalError: local variable 'a' referenced before assignment
這是因為在執行程式碼 c = foo()時,python會匯入全部的閉包函式體bar()來分析其的區域性變數,python規則指定所有在賦值語句左面的變數都是區域性變數,則在閉包bar()中,變數a在賦值符號"="的左面,被python認為是bar()中的區域性變數。再接下來執行print c()時,程式執行至a = a + 1時,因為先前已經把a歸為bar()中的區域性變數,所以python會在bar()中去找在賦值語句右面的a的值,結果找不到,就會報錯。解決的方法很簡單
def foo(): a = [1] def bar(): a[0] = a[0] + 1 return a[0] return bar
只要將a設定為一個容器就可以了。這樣使用起來多少有點不爽,所以在python3以後,在a = a + 1 之前,使用語句nonloacal a就可以了,該語句顯式的指定a不是閉包的區域性變數。
3,還有一個容易產生錯誤的事例也經常被人在介紹python閉包時提起,我一直都沒覺得這個錯誤和閉包有什麼太大的關係,但是它倒是的確是在python函數語言程式設計是容易犯的一個錯誤,我在這裡也不妨介紹一下。先看下面這段程式碼
for i in range(3):
print i
在程式裡面經常會出現這類的迴圈語句,Python的問題就在於,當迴圈結束以後,迴圈體中的臨時變數i不會銷燬
flist = []
for i in range(3):
def foo(x): print x + i
flist.append(foo)
for f in flist:
f(2)
可能有些人認為這段程式碼的執行結果應該是2,3,4.但是實際的結果是4,4,4。這是因為當把函式加入flist列表裡時,python還沒有給i賦值,只有當執行時,再去找i的值是什麼,這時在第一個for迴圈結束以後,i的值是2,所以以上程式碼的執行結果是4,4,4. 解決方法也很簡單,改寫一下函式的定義就可以了。
for i in range(3):
def foo(x,y=i): print x + y
flist.append(foo)