代码之家  ›  专栏  ›  技术社区  ›  peachykeen

如果在解释/抖动之前将Python源代码编译为字节码,那么为什么在运行时之前不捕获此错误?

  •  2
  • peachykeen  · 技术社区  · 6 年前

    我编写了以下函数:

    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-vm解释(或jits)之前,python代码被编译为字节码,那么为什么在编译期间我们看不到这个错误呢?为什么10万个数字会在我们最终看到错误之前打印出来?在纯编译语言(如C)中,我们会在编译时看到错误,那么为什么我们不在这个“部分编译”语言中看到它呢?

    2 回复  |  直到 6 年前
        1
  •  3
  •   Dinari    6 年前

    您可以使用 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将能够找到它。

        2
  •  1
  •   Owen    6 年前

    python代码被编译成字节码,然后在运行时解释该字节码。您的代码是完全有效的Python,因此它将成功编译为字节码。然而,当解释器尝试执行字节码时,该字节码将在运行时抛出一个错误。