简体   繁体   中英

If Python source code is compiled to bytecode before interpretation/JITing, why isn't this error caught before runtime?

I wrote the following function:

def f():
    for i in range(100000):
        print(i)
    some_function_that_doesnt_exist()

When I run my file, this will print out numbers in range 100000 and then raise the error: NameError: name 'some_function_that_doesnt_exist' is not defined .

This is an example of a broader question I have about Python. If the Python code is compiled to bytecode before the Python VM interprets it (or JITs it), why don't we see this error during compilation? Why are 100000 numbers printing out before we finally see an error? In a purely compiled language like C we would see the error at compile time, so why don't we see it in this "partially compiled" language?

You can look at what the python VM run, using dis :

import dis

def f():
    for i in range(100000):
        print(i)
    some_function_that_doesnt_exist()


dis.dis(f)

Will output:

  4           0 SETUP_LOOP              24 (to 26)
              2 LOAD_GLOBAL              0 (range)
              4 LOAD_CONST               1 (100000)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                12 (to 24)
             12 STORE_FAST               0 (i)

  5          14 LOAD_GLOBAL              1 (print)
             16 LOAD_FAST                0 (i)
             18 CALL_FUNCTION            1
             20 POP_TOP
             22 JUMP_ABSOLUTE           10
        >>   24 POP_BLOCK

  6     >>   26 LOAD_GLOBAL              2 (some_function_that_doesnt_exist)
             28 CALL_FUNCTION            0
             30 POP_TOP
             32 LOAD_CONST               0 (None)
             34 RETURN_VALUE

This is what the python VM actually runs. as you can see from lines 10 to 24 it actually deal with the printing.
Only when you reach line 26, the python VM tries to load the function, which results in the name error.



A bit on the difference, when compiled language do the compiling to byte-code (C to assembly for example), a function call need to know exactly where in the memory the function exists. Thus when you attempt to compile a non-existing function, it will not be able to translate it into byte code, as it wont be able to look up the function location.
Python VM can access an array of globals, which can be modified in real time, and the lookup in this array is also in real time.
Doing:

dis.dis("def some_function_that_doesnt_exist(): 1")

will result in:

  1           0 LOAD_CONST               0 (<code object some_function_that_doesnt_exist at 0x7f38dc90ed20, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('some_function_that_doesnt_exist')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (some_function_that_doesnt_exist)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE

Where line 2 load the function name from the const table, and after making the function at line 4, stores it, according to the name, at line 6. Thus when looking for that function next time, python VM will be able to find it.

Python code is compiled to bytecode and that bytecode is then interpreted at runtime. Your code is perfectly valid Python, so it will compile to bytecode successfully. That bytecode, however, will throw an error at runtime when the interpreter attempts to execute it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM