我正在尝试更清楚地理解python编译器/解释器进程。不幸的是,我没有参加口译课,也没有读过很多关于它们的内容。
基本上,我现在理解的是.py文件中的Python代码首先编译成python字节码(我假设偶尔会看到.pyc文件?)。接下来,字节码被编译成机器代码,这是处理器实际理解的语言。 差不多,我已经阅读了这个帖子Why python compile the source to bytecode before interpreting?
有人可以给我一个很好的解释整个过程,记住我对编译器/解释器的知识几乎不存在吗?或者,如果那是不可能的,也许给我一些资源,快速概述编译器/口译员?
由于
答案 0 :(得分:49)
字节码实际上并不解释为机器码,除非你使用了一些奇特的实现,比如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
的{{1}})并将其推送到解释器的堆栈(Python的解释器是一个堆栈机器)。在读取下一条指令fib
时,它从函数拥有的常量集合中获取常数1,在这种情况下恰好是数字2,并将其推送到堆栈中。你实际上可以看到这些常量:
LOAD_CONST 1
下一条指令>>> 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
(16位中的数字0)是'\x00\x00'
的参数,并告诉字节码解释器将参数0加载到堆栈上。
答案 1 :(得分:2)
要完成伟大的Marcelo Cantos's answer,这里只是一个小的逐列摘要来解释反汇编字节码的输出。
例如,给定此功能:
def f(num):
if num == 42:
return True
return False
这可能会被反汇编成(Python 3.6):
(1)|(2)|(3)|(4)| (5) |(6)| (7)
---|---|---|---|----------------------|---|-------
2| | | 0|LOAD_FAST | 0|(num)
|-->| | 2|LOAD_CONST | 1|(42)
| | | 4|COMPARE_OP | 2|(==)
| | | 6|POP_JUMP_IF_FALSE | 12|
| | | | | |
3| | | 8|LOAD_CONST | 2|(True)
| | | 10|RETURN_VALUE | |
| | | | | |
4| |>> | 12|LOAD_CONST | 3|(False)
| | | 14|RETURN_VALUE | |
每列都有特定目的:
JUMP
从之前的说明到此dis
module中简要解释,它们的实现可以在ceval.c
(CPython的核心循环)< / LI>