2016-08-01 33 views
0

我想在python中建立一個計數器與閉包的屬性。在以下工作中的代碼:蟒蛇計數器與關閉

def generate_counter(): 
    CNT = [0] 
    def add_one(): 
     CNT[0] = CNT[0] + 1 
     return CNT[0] 
    return add_one 

然而,當我改變列表CNT一個變種,它不工作:

def generate_counter1(): 
    x = 0 
    def add_one(): 
     x = x + 1 
     return x 
    return add_one 

當我打印實例的封閉性,我找到了第二種情況是__closure__沒有:

>>> ct1 = generate_counter() 
>>> ct2 = generate_counter1() 
>>> print(ct1.__closure__[0]) 
<cell at 0xb723765c: list object at 0xb724370c> 
>>> print(ct2.__closure__) 
None 

只是想知道爲什麼在外部函數的索引必須是一個列表?


感謝您的回答!找到明確解釋此問題的文檔 https://docs.python.org/3/faq/programming.html#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

+0

看起來像這是一個副本http://stackoverflow.com/questions/4851463/python-closure-write-to-variable-in-parent-scope – ChatterOne

回答

3

Python通過查看名稱綁定行爲來確定名稱的範圍;賦值就是這樣一種行爲(函數參數,導入,目標在for target ...while .. as target是其他例子)。在函數中綁定的名稱被認爲是本地。請參閱參考文檔的Naming and Binding section

所以在你的第二個例子名稱x本地變量,因爲你直接分配給它:

x = x + 1 

事實上,因爲你從來沒有給該x局部值,你會得到當您嘗試使用該功能時出現異常;本地名稱是綁定當您嘗試閱讀:

>>> generate_counter1()() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 4, in add_one 
UnboundLocalError: local variable 'x' referenced before assignment 

在你的第一個例子中,沒有這樣的結合發生;您改爲的內容CNT,該名稱引用的內容未被更改。

如果您正在使用Python 3,你可以重寫決定做一個名字的地方,通過使用nonlocal statement

def generate_counter2(): 
    x = 0 
    def add_one(): 
     nonlocal x 
     x = x + 1 
     return x 
    return add_one 

通過使x非本地,Python的發現它的父上下文,並創建再次關閉它。

>>> def generate_counter2(): 
...  x = 0 
...  def add_one(): 
...   nonlocal x 
...   x = x + 1 
...   return x 
...  return add_one 
... 
>>> generate_counter2().__closure__ 
(<cell at 0x1078c62e8: int object at 0x1072c8070>,) 

nonlocal是Python 3中的新增功能;在Python 2中,你僅限於使用可變列表對象來逃避綁定規則的技巧。另一個技巧是將計數器分配給嵌套函數的屬性;再次,這避免了在當前範圍綁定的名稱:

def generate_counter3(): 
    def add_one(): 
     add_one.x += 1 
     return add_one.x 
    add_one.x = 0 
    return add_one 
0

它不是一個列表,它只是必須是一個可變對象,你變異,而不是重新分配。

docs

如果一個變量的函數體中的任何位置分配一個值,它假定是本地除非明確聲明爲全局的。

因此,在第二個示例中,x被認爲是本地(對於內部函數),並且在第一次賦值後,您將映射外部'x'。

另一方面,在第一個示例中(因爲您沒有爲CNT賦值),它在外函數中定義的CNT上運行。

+1

「它只是一個不可變的對象」我想你意味着可變嗎? – roganjosh

+1

@roganjosh的確我做到了!感謝您的更正:) – jedwards