deppep
deppep

Reputation: 176

A function returns another function. Why the function inside can scope a list from the parent but not a constant?

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?

Upvotes: 0

Views: 71

Answers (3)

QuarticCat
QuarticCat

Reputation: 1526

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

Upvotes: 1

ZaO Lover
ZaO Lover

Reputation: 433

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

Upvotes: 0

rassar
rassar

Reputation: 5670

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

Upvotes: 1

Related Questions