python counter with closure

后端 未结 2 479
醉梦人生
醉梦人生 2021-01-06 13:58

I\'m trying to build a counter in python with the property of closure. The code in the following works:

def generate_counter():
    CNT = [0]
    def add_one         


        
相关标签:
2条回答
  • 2021-01-06 14:29

    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.

    0 讨论(0)
  • 2021-01-06 14:45

    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
    
    0 讨论(0)
提交回复
热议问题