0

I have read the following posts but I am still unsure of something.

  1. Python Compilation/Interpretation Process

  2. Why python compile the source to bytecode before interpreting?

If I have a single Python file myfunctions.py containing the following code.

x  = 3
def f():
    print x
    x = 2

Then, saying $ python myfunctions.py runs perfectly fine. But now make one small change to the above file. The new file looks as shown below.

x  = 3
def f():
    print x
    x = 2
f() # there is a function call now

This time, the code gives out an error. Now, I am trying to understand this behavior. And so far, these are my conclusions.

  • Python creates bytecode for x=3
  • It creates a function object f, quickly scans and has bytecode which talks about the local variables within f's scope but note that the bytecode for all statements in Python are unlikely to have been constructed.
  • Now, Python encounters a function call, it knows this function call is legitimate because the bare minimum bytecode talking about the function object f and its local variables is present.
  • Now the interpreter takes the charge of executing the bytecode but from the initial footprint it knows x is a local variable here and says - "Why are you printing before you assign?"

Can someone please comment on this? Thanks in advance. And sorry if this has been addressed before.

Community
  • 1
  • 1
Killer
  • 417
  • 3
  • 10
  • 2
    I assume the extra indentation isn't in your actual file and that the exception is an `UnboundLocalError`? – mgilson Feb 24 '14 at 04:33
  • What's the error? Is the indentation supposed to be broken on the "def" line in the second example? – tripleee Feb 24 '14 at 04:34
  • The error I get (after fixing the indentation) is `UnboundLocalError: local variable 'x' referenced before assignment` – Mike DeSimone Feb 24 '14 at 04:35
  • Extremely sorry for that silly mistake. I have fixed the indentation. That was not the problem. The problem concerns scopes. The error is about Python complaining that the variable is used before it is assigned. – Killer Feb 24 '14 at 04:48

2 Answers2

2

When the interpreter reads a function, for each "name" (variable) it encounters, the interpreter decides if that name is local or non-local. The criteria that is uses is pretty simple ... Is there an assignment statement anywhere in the body to that name (barring global statements)? e.g.:

def foo():
    x = 3  # interpreter will tag `x` as a local variable since we assign to it here.

If there is an assignment statement to that name, then the name is tagged as "local", otherwise, it gets tagged as non-local.

Now, in your case, you try to print a variable which was tagged as local, but you do so before you've actually reached the critical assignment statement. Python looks for a local name, but doesn't find it so it raises the UnboundLocalError.

Python is very dynamic and allows you to do lots of crazy things which is part of what makes it so powerful. The downside of this is that it becomes very difficult to check for these errors unless you actually run the function -- In fact, python has made the decision to not check anything other than syntax until the function is run. This explains why you never see the exception until you actually call your function.


If you want python to tag the variable as global, you can do so with an explicit global1 statement:

x = 3
def foo():
  global x
  print x
  x = 2

foo()  # prints 3
print x  # prints 2

1python3.x takes this concept even further an introduces the nonlocal keyword

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • Thank you for the answer. But when Python encounters a function definition, it is clear that it is doing some scope checking and keeping track of variables that WILL BE USED when the function is called. Otherwise, the first x it encounters would have been the global x(=3), right? And then a local x(=2) would have sprung into existence? But Python is clearly analyzing the entire function body for which variables are local and which are global even though the statements are not executed until the function is called. – Killer Feb 24 '14 at 04:52
  • @Killer -- Correct. It bins variables by local or nonlocal *when the function is read*, not when it gets executed. – mgilson Feb 24 '14 at 04:57
1

mgilson got half of the answer.

The other half is that Python doesn't go looking for errors beyond syntax errors in functions (or function objects) it is not about to execute. So in the first case, since f() doesn't get called, the order-of-operations error isn't checked for.

In this respect, it is not like C and C++, which require everything to be fully declared up front. It's kind of like C++ templates, where errors in template code might not be found until the code is actually instantiated.

Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96