3

I get an unexpected behavior of python execution.

if True:
    print("Hello")
else:
    I am an error. what can you do about it?

Now, this code doesn't raise a SyntaxError because the control never goes into the else statement. In compiled languages like C++ it gets an error. Even in Java, unused code is an error. But not in Python.

Now in this case:

x = 10
def foo():
    print(x)
    x += 1

The print statement raises an UnboundLocalError as specified here. According to the previous logic, this error should not be until the control encounters x+=1. But it does, like in any other compiled language.

Then how to determine when will the code run as compiled or interpreted?

Edit: If it is compiled into a bytecode .pyc file and then interpreted. Then why is the first example's else statement not detected?

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
Abhishek Bera
  • 121
  • 1
  • 5

4 Answers4

2

Opposite. Python compiles source into byte code before execution. The first case will raise a syntax error during compilation phase.

In the second case, the compiler sees that x is modified in the function so it binds x to the function object. The function namespace is created each time the function is called but the variable only appears when it has been assigned. Its only when you execute print(x) that python realizes you are asking for a local variable that hasn't been assigned. This is a common bug when not all execution paths set a variable like they should.

Slightly modifying the example, sometimes the local variable is set, other times it is not. When set, you see the variable in locals and the print works. When not set, the variable is not in locals and the print fails.

x = 10
def foo(val):
    if val:
        x = 1
    print(val, 'before', locals())
    print(x)
    print(val, 'after')
    x += 1

foo(True)
foo(False)

Output

True before {'val': True, 'x': 1}
1
True after
False before {'val': False}
Traceback (most recent call last):
  File "o.py", line 11, in <module>
    foo(False)
  File "o.py", line 6, in foo
    print(x)
UnboundLocalError: local variable 'x' referenced before assignment
tdelaney
  • 73,364
  • 6
  • 83
  • 116
1

It always runs as interpreted; but the syntax checker is invoked on the entire file as the first step, which generates the bytecode file.

In both python 2.7 and 3.5 the code you've put at the top will result in a syntax error:

python3 junk.py
  File "junk.py", line 4
    I am an error. what can you do about it?
              ^
SyntaxError: invalid syntax

It shouldn't be possible to get a 'runtime' syntax error in python; the only way to achieve that is to dynamically import a module with a syntax error.

I'm not sure I've understood your question, but the error in the second case is a runtime error; it's like doing int x = 0; y = 10/x; in C; variable scope ('does x exist at this point?') is not resolved during the syntax parse in python.

--

Edit; here's a dump from my terminal:

Clank:tmp doug$ cat junk.py
if True:
    print("Hello")
else:
    I am an error. what can you do about it?

Clank:tmp doug$ python junk.py
  File "junk.py", line 4
    I am an error. what can you do about it?
       ^
SyntaxError: invalid syntax

Clank:tmp doug$ python3 junk.py
  File "junk.py", line 4
    I am an error. what can you do about it?
       ^
SyntaxError: invalid syntax
Doug
  • 32,844
  • 38
  • 166
  • 222
  • can you show me the code, because I know the first one doesn't cause a syntax error. The error is only when the control reaches the statement. And that's where my problem lies. – Abhishek Bera Nov 27 '16 at 07:32
  • @AbhishekBera Added it to the question. I'm mystified how you might be able to get that to run without it being a syntax error. Can you provide any other details? What python version are you using & what platform? – Doug Nov 27 '16 at 07:35
  • Ok.. I think I might have figured out what went wrong.. When I wrote the first one in the live interpreter, it works fine. But when I wrote the code in a file, the SyntaxError finally appeared. The second one is scope shadowing. There is a variable x in the global scope but whenever it encounters an edit like x+=1, it looks up x only in the local scope and all others get eliminated. Since there is no variable in the local scope we have the UnboundLocalError. – Abhishek Bera Nov 27 '16 at 07:55
1

Okay after reading answers from Doug and Jim, I think I have an idea on how this works. First of all of the examples work in REPL (Ipython, default)

Files: If you write this in a file:

if True:
    print("Hi")
else:
    I am an error. What can you do about it?

And run the file, it will throw a SyntaxError. This proves that whenever we execute a python code from a file, it generates a bytecode and since the statement in else is not a valid python expression, we get a SyntaxError.

REPL: With REPL things get a bit dependent. In the python interpreter if you type

>>>def foo():
       if True:
           print("Hey")
       else:
           I am an error. What can you do about it?
>>>foo()
Hey

A successful execution means no byte code right? Hold on.

If you write this:

>>>x = 10
>>>def foo():
       print(x)
       x += 1
>>>foo()

And Boom! everything falls apart, You get UnboundLocalError at print(x) statement. Which means bytecode is there.

So what exactly is happening here?

If python finds one single occurrence of a variable, it tries to optimize its working by reading all of them first. So, in the second example when the code encounters print(x), it tries to lookup all the operations on x. Soon it finds the statement x+=1. Since there is no mention of x in the local scope and python never looks for the variable in global scope if not mentioned explicitly, we have

UnboundLocalError: local variable 'x' is referenced before assignment 

Conclusive Proof

If we write something like this:

>>>x = 10
>>>def foo():
      if True:
          print(x)
      else:
          x+=1
>>>foo()
UnboundLocalError: local variable 'x' referenced before assignment

That's it!

x+=1 was never going to be executed but since the print statement prints x and another reference(x+=1) was the issue, there was an error encountered before printing the value. The first case worked fine without the SyntaxError in REPL because it never bothered looking inside the else statement because it never mattered.

Abhishek Bera
  • 121
  • 1
  • 5
0

If you are running this through an interactive REPL of some short (and not Pythons default REPL) you might be getting this to actually execute without errors (IPythons' qtconsole allows it w/o issue for example). Why this is allowed depends solely on the REPL and its implementation.

In Python, this is an SyntaxError; there can't be any bytecode generated for it:

s ="""
i= 1
if i:
    print("Hello")
else:
    I am an error. what can you do about it?
"""
c = compile(s, '', mode='exec')
  File "<string>", line 6
    I am an error. what can you do about it?
       ^
SyntaxError: invalid syntax

The Grammar specifically doesn't allow it, only string literals are allowed to be placed with spaces between them (which Python later concatenates), names aren't (a single name on it's own is of course allowed). In short, this fails at the parsing phase:

from parser import suite
st = suite(s)
  File "<string>", line 6
    I am an error. what can you do about it?
       ^
SyntaxError: invalid syntax

Python has no idea what to do when it sees two names separated by a space, a space doesn't imply any operation (except for str literals).

Community
  • 1
  • 1
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Yes, I am using Ipython. But even if I do both the codes in REPL. There is a discrepancy between the first and the second. My question is why is that discrepancy. Actually, I am not able to get a unique theory to explain both the cases. I have also tried in Python2.7 and 3.5's default REPL and the results were the same... Why are the two statements behaving differently? – Abhishek Bera Nov 27 '16 at 12:44