Python编译/解释过程
我试图更清楚地了解python编译器/解释器的过程。不幸的是,我没有上过口译课,也没有读过很多关于口译的文章。
基本上,我现在所了解的是,.py文件中的Python代码首先被编译为python字节码(我认为这是我偶尔看到的.pyc文件?)。接下来,字节码被编译成机器码,这是处理器真正理解的语言。差不多,我已经读过这个线程。为什么python在解释之前将源代码编译为字节码?
考虑到我对编译器/解释器的知识几乎不存在,有人可以给我一个关于整个过程的很好的解释吗?或者,如果不可能的话,也许给我一些资源,以快速概述编译器/解释器?
谢谢
-
除非您使用某些特殊的实现(例如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加载到堆栈上。