Python编译/解释过程

发布于 2021-01-29 19:16:10

我试图更清楚地了解python编译器/解释器的过程。不幸的是,我没有上过口译课,也没有读过很多关于口译的文章。

基本上,我现在所了解的是,.py文件中的Python代码首先被编译为python字节码(我认为这是我偶尔看到的.pyc文件?)。接下来,字节码被编译成机器码,这是处理器真正理解的语言。差不多,我已经读过这个线程。为什么python在解释之前将源代码编译为字节码?

考虑到我对编译器/解释器的知识几乎不存在,有人可以给我一个关于整个过程的很好的解释吗?或者,如果不可能的话,也许给我一些资源,以快速概述编译器/解释器?

谢谢

关注者
0
被浏览
42
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    除非您使用某些特殊的实现(例如pypy),否则字节码实际上不会解释为机器代码。

    除此之外,您的描述正确。字节码被加载到Python运行时中,并由虚拟机解释,该虚拟机是一段代码,它读取字节码中的每条指令并执行所指示的任何操作。您可以在dis模块中看到此字节码,如下所示:

    >>> def fib(n): return n if n < 2 else fib(n - 2) + fib(n - 1)
    ... 
    >>> fib(10)
    55
    >>> import dis
    >>> dis.dis(fib)
      1           0 LOAD_FAST                0 (n)
                  3 LOAD_CONST               1 (2)
                  6 COMPARE_OP               0 (<)
                  9 JUMP_IF_FALSE            5 (to 17)
                 12 POP_TOP             
                 13 LOAD_FAST                0 (n)
                 16 RETURN_VALUE        
            >>   17 POP_TOP             
                 18 LOAD_GLOBAL              0 (fib)
                 21 LOAD_FAST                0 (n)
                 24 LOAD_CONST               1 (2)
                 27 BINARY_SUBTRACT     
                 28 CALL_FUNCTION            1
                 31 LOAD_GLOBAL              0 (fib)
                 34 LOAD_FAST                0 (n)
                 37 LOAD_CONST               2 (1)
                 40 BINARY_SUBTRACT     
                 41 CALL_FUNCTION            1
                 44 BINARY_ADD          
                 45 RETURN_VALUE        
    >>>
    

    详细说明

    了解上面的代码永远不会由您的CPU执行非常重要。它也永远不会转换成某种东西(至少不是在Python的官方C实现上)。CPU执行虚拟机代码,该虚拟机代码执行字节码指令指示的工作。当解释器要执行该fib功能时,它会一次读取一条指令,然后执行指令。它查看第一条指令,LOAD_FAST 0从而从保存参数的任何地方获取参数0(n传递给fib),并将其压入解释器的堆栈(Python的解释器是堆栈计算机)。在阅读下一条说明时,LOAD_CONST 1,它将从该函数拥有的常量集合中获取一个常量1(在这种情况下恰好是2),并将其压入堆栈。您实际上可以看到以下常量:

    >>> fib.func_code.co_consts
    (None, 2, 1)
    

    下一条指令,COMPARE_OP 0告诉解释器弹出两个最顶部的堆栈元素,并在它们之间进行不等式比较,将布尔结果推回堆栈。第四条指令基于布尔值确定是向前跳五条指令还是继续下一条指令。所有这些动词都解释if n < 2了条件表达式中的部分fib。弄清楚其余fib字节码的含义和行为,对您而言将是非常有启发性的练习。唯一的一个,我不知道的是POP_TOP,我猜想JUMP_IF_FALSE已定义为将其布尔参数保留在堆栈上而不是将其弹出,因此必须显式弹出它。

    更具指导意义的是检查原始字节码,fib从而:

    >>> code = fib.func_code.co_code
    >>> code
    '|\x00\x00d\x01\x00j\x00\x00o\x05\x00\x01|\x00\x00S\x01t\x00\x00|\x00\x00d\x01\x00\x18\x83\x01\x00t\x00\x00|\x00\x00d\x02\x00\x18\x83\x01\x00\x17S'
    >>> import opcode
    >>> op = code[0]
    >>> op
    '|'
    >>> op = ord(op)
    >>> op
    124
    >>> opcode.opname[op]
    'LOAD_FAST'
    >>>
    

    因此,您可以看到字节码的第一个字节是LOAD_FAST指令。下一对字节'\x00\x00'(16位中的数字0)是的参数LOAD_FAST,并告诉字节码解释器将参数0加载到堆栈上。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看