0

Consider this function:

def g():
    x = []
    
    def f():
        x.append([0])
        print(x)
        pass
    return f

Calling it:

test = g()
test()

I get the following output:

Out: [[0]]

We can reinitialize the test function and call it multiple times:

test = g()
for i in range(3):
    test()

Resulting in the following output:

Out: [[0]]
[[0], [0]]
[[0], [0], [0]]

However, defining the following function:

def j():
    x = 1
    def f():
        x += 1
        print(x)
        pass
    return f

And calling it:

test = j()
test()

Results in an error:

UnboundLocalError: local variable 'x' referenced before assignment

The list seems to be in the inner function scope while the value is not. Why is this happening?

deppep
  • 135
  • 1
  • 1
  • 7
  • Does this answer your question? [Why can't Python increment variable in closure?](https://stackoverflow.com/questions/21959985/why-cant-python-increment-variable-in-closure) – Michael Szczesny Sep 21 '20 at 14:50

3 Answers3

1

This is because j uses an assignment expression, where g uses a method call. Remember that x += 1 is equivalent to x = x + 1. If you change g to:

def g():
    x = []

    def f():
        x += [[0]]
        print(x)
        pass
    return f

You get:

>>> test = g()
>>> test()
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    test()
  File "<pyshell#18>", line 5, in f
    x += [[0]]
UnboundLocalError: local variable 'x' referenced before assignment
rassar
  • 5,412
  • 3
  • 25
  • 41
  • Thank you. Why assignment expression are not callable inside the child function but method calls are? – deppep Sep 21 '20 at 14:54
  • @deppep assignment *marks a variable as **local** at **compile time***. So if you don't want it to be treated as local, you need to use either `nonlocal` or `global`. This is the same even with un-nested functions. – juanpa.arrivillaga Sep 21 '20 at 15:52
1

@rassar clearly explained the reason. Here I give a solution:

def j():
    x = 1
    def f():
        nonlocal x
        x += 1
        print(x)
        pass
    return f

Actually this is not a simple question. Even though += looks like a bounded method call, an in-place operation (if you have experience in other languages). But what runs under is something like x = x.__add__(1) or x = x.__iadd__(1). So this is an assignment.

QuarticCat
  • 1,314
  • 6
  • 20
  • Thank you! Why assignment expression are not callable inside the child function but method calls are? – deppep Sep 21 '20 at 14:55
  • It's because python mixed the creation and use of variables. That is, `x = y` may refer to "creating a variable `x` with value `y`" or "assign value `y` to existing variable `x`". Calling a method is deterministic, meaning "use". `x = y` is also deterministic, meaning "creation". But `x += y` is ambiguous. There are both "creation" and "use" and Python thinks they referred to the same variable. – QuarticCat Sep 21 '20 at 15:07
  • UPD: `x = y` is also deterministic, but depending on the context. When it first comes up in a scope, it means "creation". So `x += y` i.e. `x = x + y` uses the inner scope variable `x` rather than outer `x`. @deppep – QuarticCat Sep 21 '20 at 15:37
  • @QuarticCat `x += y` isn't ambiguous it is simply both. – juanpa.arrivillaga Sep 21 '20 at 15:53
0

Because, the python complier knows that the variable 'x' in function f is a local variable not in the function j. So the error you mentioned above is occurred.

So you should use nonlocal. Please refer the link below. Accessing parent function's variable in a child function called in the parent using python

ZaO Lover
  • 433
  • 2
  • 13