2016-08-01 35 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