首页 > 解决方案 > 如果 Python 源代码在解释/JITing 之前编译为字节码,为什么在运行之前没有捕获到这个错误?

问题描述

我写了以下函数:

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

当我运行我的文件时,这将打印出 100000 范围内的数字,然后引发错误:NameError: name 'some_function_that_doesnt_exist' is not defined.

这是我对 Python 提出的更广泛问题的一个示例。如果 Python 代码在 Python VM 解释它(或 JIT)之前被编译为字节码,为什么我们在编译过程中看不到这个错误?为什么在我们最终看到错误之前打印出 100000 个数字?在像 C 这样的纯编译语言中,我们会在编译时看到错误,那么为什么我们不在这种“部分编译”的语言中看到它呢?

标签: pythoncompilationinterpreter

解决方案


您可以使用以下命令查看 python VM 运行的内容dis

import dis

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


dis.dis(f)

将输出:

  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

这就是 python VM 实际运行的内容。从第 10 行到第 24 行可以看出,它实际上处理的是打印。
只有当您到达第 26 行时,python VM 才会尝试加载该函数,这会导致名称错误。



有点不同的是,当编译语言编译为字节码(例如 C 到汇编)时,函数调用需要确切地知道函数在内存中的位置。因此,当您尝试编译一个不存在的函数时,它将无法将其转换为字节码,因为它无法查找函数位置。
Python VM可以访问一个全局数组,可以实时修改,在这个数组中查找也是实时的。
正在做:

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

将导致:

  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

其中第 2 行从 const 表中加载函数名,在第 4 行创建函数后,将其根据名称存储在第 6 行。这样下次查找该函数时,python VM 将能够找到它.


推荐阅读