1. 程式人生 > >以下這段程式碼是在python中使用閉包時一段經典的錯誤程式碼

以下這段程式碼是在python中使用閉包時一段經典的錯誤程式碼

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不會銷燬

,而是繼續存在於執行環境中。還有一個python的現象是,python的函式只有在執行時,才會去找函式體裡的變數的值。

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)