1
def counter(x):

    def _cnt():
        #nonlocal x
        x = x+1
        print(x)
        return x

    return _cnt
a = counter(0)
print(a())

Above code gives the following error

UnboundLocalError: local variable 'x' referenced before assignment

Why this is not able to create a new object with value 'x+1' in the namespace of _cnt and bind it to x. we will have reference x in both function namespaces

user634615
  • 647
  • 7
  • 17

2 Answers2

3

As soon as you assign to a name in a given scope, all references to the same name inside the same scope are local. Hence x + 1 cannot be evaluated (as it tries to reference the local x).

Hence this works:

def f():
    x = 42
    def g():
        print(x)
    g()
f()

But this doesn't:

def f():
    x = 42
    def g():
        print(x)
        x = 42
    g()
f()

The first print has this bytecode:

0 LOAD_GLOBAL              0 (print) 
3 LOAD_DEREF               0 (x) 
6 CALL_FUNCTION            1 
9 POP_TOP  

while the second print has this one:

0 LOAD_GLOBAL              0 (print) 
3 LOAD_FAST                0 (x) 
6 CALL_FUNCTION            1 
9 POP_TOP 
Hyperboreus
  • 31,997
  • 9
  • 47
  • 87
  • I think an assignment statement like 'x = x + 1' is executed like this. First expression x + 1 is evaluated (so x is refering to counter's x) and then the return value of the expression is assigned to x (this should create a new reference in _cnt's namespace – user634615 Sep 28 '13 at 05:43
  • As you can see in my second example, as soon as you assign to a name, all reference to it become local, no matter if they happen before or after the assignment. – Hyperboreus Sep 28 '13 at 05:45
  • @user634615 I added the disassemblies. The `LOAD_FAST` is what fails. – Hyperboreus Sep 28 '13 at 05:48
  • it means the flow of assignment statment differs according to where it is used. That is a bit surprising – user634615 Sep 28 '13 at 05:57
  • 2
    @user634615: name locality is defined at *compile time*, not at execution time. Expression execution order has no influence on that. – Martijn Pieters Sep 28 '13 at 06:48
  • @MartijnPieters then why above code works if x is mutable obect – user634615 Sep 28 '13 at 18:05
  • 1
    @user634615: Because you are not changing the `x` name itself. It remains unchanged; it is bound to an object and only that *object* changes. – Martijn Pieters Sep 28 '13 at 18:09
1

The scopes of function counter and _cnt are not the same. Even though they're nested, it doesn't matter.

So the x in counter does not exist in _cnt. Perhaps pass it as an argument (or use nonlocal, as you seemed to have understood)

TerryA
  • 58,805
  • 11
  • 114
  • 143