python counter with closure

你离开我真会死。 提交于 2019-12-04 17:36:42

Python determines the scope of a name by looking at name binding behaviour; assignment is one such behaviour (function parameters, importing, the target in for target ... or while .. as target are other examples). A name you bind to in a function is considered local. See the Naming and Binding section of the reference documentation.

So the name x in your second example is a local variable, because you assigned directly to it:

x = x + 1

In fact, because you never gave that x a local value, you'll get an exception when you try to use that function; the local name is unbound when you try to read it:

>>> 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

In your first example no such binding takes place; you are instead altering the contents of CNT, what that name references is not altered.

If you are using Python 3, you can override the decision to make a name local, by using a nonlocal statement:

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

By making x non-local, Python finds it in the parent context and creates a closure for it again.

>>> 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 is new in Python 3; in Python 2 you are limited to tricks like using a mutable list object to evade the binding rule. Another trick would be to assign the counter to an attribute of the nested function; again, this avoids binding a name in the current scope:

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

It doesn't have to be a list, it just has to be an mutable object which you mutate, not reassign.

From the docs:

If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.

Thus, in your second example, x is considered local (to the inner function), and, after your first assignment, you're shadowing the outer 'x'.

On the other hand, in the first example (since you don't assign a value to CNT) it operates on the CNT defined in the outer function.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!