2010-03-25 25 views
8

爲什麼這項工作:作用域錯誤

def function1():                            
     a = 10                              
     def function2(): 
      print a 
     function2() 

但這並不:

def function1(): 
    a = 10 
    def function2(): 
     print a 
     a -= 1 
     if a>0: 
      function2() 
    function2() 

我得到這個錯誤:

UnboundLocalError: local variable 'a' referenced before assignment 

回答

13

似乎誤差不很描述根本問題。邁克解釋了這些信息,但這並不能解釋根本原因。

實際的問題是,在python中,你不能指定關閉的變量。所以在函數2中,'a'是隻讀的。當你指定它時,你創建了一個新的變量,正如Mike指出的那樣,在你寫之前你已經讀過了。

如果你想從內部範圍分配給到外變量,你必須欺騙,像這樣:

def function1(): 
    al = [10] 
    def function2(): 
     print al[0] 
     al[0] -= 1 
     if al[0]>0: 
      function2() 
    function2() 

所以人是不可改變的,但它的內容不是,你可以改變他們,而無需創建新變量。

+2

實際上,這是在設計這個功能,你不能分配給非本地範圍的關鍵點。 (注意:'al'是* mutable *;這就是爲什麼這是有效的。) – 2010-03-25 15:36:44

+2

爲了清楚起見,我認爲重要的是區分變量和al包含的值。它總是回到我的指針,所以讓我說這個;您不能將al指向新列表,但可以更改al指向的列表的內容。 al - > [v1,v2,v3] al不能更改,但v1,v2和v3可以更改。 Mike絕對正確,這使得al可變,因爲在我們的術語中al *是*列表而不是指向列表的指針。 – charlieb 2010-03-25 16:21:56

+0

+1非常好的答案。 – 2010-03-26 04:39:44

2

在非工作片段,當您說「a -= 1」時,您分配給a。因此,function2a在本地範圍內,不在封閉範圍內。 Python的閉包是詞彙 - 它不會動態地在function2的框架中查找a,並且它沒有被分配,請在function1的框架中查找它。

請注意,這不取決於遞歸性或使用閉包。考慮這個功能

def foo(): 
    print a 
    a = 4 

的調用它可以把你的UnboundLocalError過的例子。 (如果沒有a = 4,它將使用全球a或者如果沒有,則提出NameError。)因爲a可能在該範圍內分配,所以它是本地的。


如果我設計這個功能,我可能會使用的方法更像

def function1(): 
    a = 10 
    def function2(a=a): 
     print a 
     a -= 1 
     if a > 0: 
      function2(a) 
    function2() 

(或課程for a in xrange(10, -1, -1): print a ;-))的

5

應該指出,這是Python中的語法錯誤。 Python本身(在字節碼級別)可以分配給這些變量就好了;在2.x中根本沒有語法來表示你想這樣做。它假定如果你在嵌套層次上賦值給變量,你的意思是它是一個局部變量。

這是一個巨大的缺點;能夠分配到閉包是最基本的。我已經用charlieb的黑客多次解決了這個問題。

Python 3中修復了這個用很笨拙爲「非本地」的文章:

def function1(): 
    a = 10 
    def function2(): 
     nonlocal a 
     print(a) 
     a -= 1 
     if a>0: 
      function2() 
    function2() 
function1() 

這是非常差的,這句法僅在3.x中可用;大多數人被困在2.x中,並且必須繼續使用黑客來解決這個問題。這非常需要回溯到2.x.

http://www.python.org/dev/peps/pep-3104/