2011-07-26 55 views
6

看一看這段代碼:Python和關閉變量

def closure(): 
    value = False 

    def method_1(): 
     value = True 

    def method_2(): 
     print 'value is:', value 

    method_1() 
    method_2() 

closure() 

我希望它來打印「值:真」,但事實並非如此。這是爲什麼?解決方案是什麼?

+0

此主題是爲什麼五分鐘免費編輯有時很糟糕的完美例子。 – agf

+0

@agf:爲什麼它不好? – Cameron

回答

16

發生這種情況是因爲method_1在聲明變量的地方有自己的本地作用域。 Python看到value = True,並認爲你正在創建一個名爲value的新變量,本地地址爲method_1

Python這樣做的原因是爲了避免使用內部函數中的變量來污染外部作用域的本地。 (您不希望在常規的模塊級函數中進行賦值,導致創建全局變量!)

如果您沒有分配給value,那麼Python將搜索外部範圍尋找變量(因此讀取該變量按預期工作,如method_2所示)。要解決這個問題

一種方式是通過使用可變對象,而不是分配新建分配FY的:

result = { 'value': False } 

def method_1(): 
    result['value'] = True 

在Python 3,nonlocal statement(見docs)加入正是這種情況:

def method_1(): 
    nonlocal value 
    value = True # Works as expected -- assigns to `value` from outer scope 
2

method_1中,Python假設(相當明智!)value是一個局部變量。每當你在一個函數內賦值給一個變量名時,假定該變量名是一個新的局部變量。如果你希望它是全局的,那麼你必須聲明它爲global,如果你希望它是「非本地的」,在Python 3中,你可以聲明它爲nonlocal,但在Python 2中,你必須做一些事情:將值存儲在容器中。這避免了必須重新分配變量名稱,從而避免了範圍模糊。

def method_1_global(): 
    global value 
    value = True 

def method_1_nonlocal_P3(): 
    nonlocal value 
    value = True 

value = [False] 
def method_1_nonlocal_P2(): 
    value[0] = True 
+0

請注意,對於'global',它不再是閉包的一部分,它可以在程序中的其他地方搞亂。 – agf

+0

@agf - 相當如此;我只是試圖儘可能以一般方式解釋範圍規則。 – senderle

2

當您分配給變量時,它假定該變量屬於本地範圍。所以valuemethod_1不是valueclosure

如果你想讓它在Python 3上工作,請在method_1上添加一行:nonlocal value

在Python 2中,

def closure(): 
    value = [False] 

    def method_1(): 
     value[0] = True 

    def method_2(): 
     print 'value is:', value 

    method_1() 
    method_2() 

closure() 

是一個可能的方法。

2

這是因爲你實際上沒有修改閉合變量 - 你用一個具有相同名稱的新變量掩蓋它。在Python 2.x中沒有簡單的解決方法,這就是爲什麼nonlocal關鍵字被添加到python 3.

但是,這可以解決使用像列表和字典這樣的可變類型。有一個good example in this answer

1

爲了避免這種情況,您可以使用列表。

value = [False] 

def method_1(): 
    value[0] = True 

什麼Python不現在在更高水平範圍的搜索爲是不是在局部變量可用。由於是一個列表,Python將其引用爲相對於* method_1 *的全局變量,因此您可以將的值視爲,它是一個列表。