3

Why is the following code invalid:

def foo1(x=5):
    def bar():
        if x == 5:
            x = 6
        print(x)
    bar()

While this code is valid:

def foo2(x=5):
    def bar():
        if x == 5:
            print('ok')
        print(x)
    bar()

foo2() will do exactly what you expect, but foo1() will give a UnboundLocalError: local variable 'x' referenced before assignment at the line if x == 5:. Why does altering the value of x later on in the code make this conditional invalid?

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
Kahr Kunne
  • 332
  • 1
  • 3
  • 14
  • 1
    Python first looks for assignment operations in the code. From the moment that happens, it is considered a local variable, that is indeed a (not so very elegant) result from that. – Willem Van Onsem Jun 18 '17 at 16:02
  • Does this answer your question? [Python inner functions](https://stackoverflow.com/questions/18616084/python-inner-functions) – user202729 Feb 01 '21 at 01:05

1 Answers1

4

Python needs first to detect what variables are local, and which variable are fetched from an outer scope. In order to do that it looks for assignments, like:

def foo1(x=5):
    def bar():
        if x == 5:
            x = 6 # an assignment, so local variable
        print(x)
    bar()

The point is, that the assignment can happen anywhere. For instance on the last line. Nevertheless, from the moment there is an assignment somewhere x is local. So in your first code fragment, x is a local variable. But you fetch it before it is assigned (bounded), so Python will error on it.

In you can use the nonlocal keyword to access x from an outer scope:

def foo1(x=5):
    def bar():
        nonlocal x
        if x == 5:
            x = 6
        print(x)
    bar()

For , you can for instance assign the variable to the function, like:

def foo1(x=5):
    def bar():
        if bar.x == 5:
            bar.x = 6
        print(bar.x)
    bar.x = x
    bar()

Note however that the two are not equivalent. Since in the former if you alter x, it will be alter the x in the foo1 scope as well. In the latter example you only modify bar.x. Of course if these are mutable objects, you alter the same object.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555