1

I have this code which works as expected:

foo = 123

def fooBar():
    print(foo)   ### cannot find foo from local scope, checks prev and prints 123
    #foo = 987
    #print(foo)

fooBar() 

But when I change the code to the following,

foo = 123

def fooBar():
    print(foo)   ### blows up here... 
    foo = 987
    print(foo)
    
fooBar() 

it blows up on the print statement:

Traceback (most recent call last):
  File "python", line 9, in <module>
  File "python", line 4, in fooBar
UnboundLocalError: local variable 'foo' referenced before assignment

I know I can get around the problem using global foo inside my function, but the error I was expected was not on line number I was expecting. I was assuming it would still print 123 and then blow up when I try to assign another value to foo.

It seems as if Python parses fooBar() and knows I do an assignment of foo later in local function and then blows up on the print, instead of grabbing the global value? Is that right?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • 1
    the linked duplicate is really close, and the accepted answer explains exactly this interesting point. The byte-compiler sees the use of `foo` as local, thus it uses this local reference in the whole function. So it's not as dynamic as we thought (I learned that myself very recently) – Jean-François Fabre May 30 '18 at 11:27
  • 1
    Local variables are defined at function level, and their names exist since the beginning of the function. `print foo` in the second snippet refers always to the local `foo` which, at the point of the first line, has not been assigned any value yet. – jdehesa May 30 '18 at 11:28
  • 2
    FWIW : "Almost like Python interpreter parses fooBar() and knows I do an assignment of foo later in local function" is __exactly__ what happens. Python is dynamic indeed but it's still compiled to bytecode (just like Java), not naiveley interpreted line-by-line like you might believe. – bruno desthuilliers May 30 '18 at 11:28
  • 2
    Your theory is correct. The first version works because Python can't find `foo` in `fooBar`'s locals, so it looks in the globals. But in the 2nd version it knows that `foo` must be local to `fooBar` because it makes an assignment to it, and there's no `global` directive. – PM 2Ring May 30 '18 at 11:28
  • I'm on board but one other question: in the above example foo's a primitive type, and if I change to a list and change foo=987 to foo.append(987) it works. So when the byte-compiler compiles does it check global or does it just check primitive assignemtns? Works: foo = [1] def fooBar(): print foo foo.append(2) print foo fooBar() – KevinSoltis May 30 '18 at 12:28
  • @KevinSoltis the compiler checks for *any assignment* and marks the *variable* as local. Note, **there are no primitive types in Python** – juanpa.arrivillaga Jun 09 '18 at 00:29

0 Answers0