Python解释器
目录Python程序的运行
首先执行词法分析、语法解析和编译,将Python代码转化成_code object_
,这是解释器可以理解的指令,而Python解释器要做的就是解释_code object_
中的指令。
字节码
>>> import dis
>>> def add() -> int:
... a = 1
... b = 2
... return a + b
...
>>> dis.dis(add)
2 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 2 (2)
6 STORE_FAST 1 (b)
4 8 LOAD_FAST 0 (a)
10 LOAD_FAST 1 (b)
12 BINARY_ADD
14 RETURN_VALUE
由此可以得到,字节码的结构为行号 | 指令在函数中的偏移 | 指令 | 指令参数 | 实际参数值
在得到的字节码中,LOAD_CONST, STORE_FAST, LOAD_FAST
等是Python的指令名称。除此之外,操作变量的字节码还有
LOAD_CONST
:加载const
变量LOAD_FAST
:加载局部变量的值STORE_FAST
:保存值到局部变量LOAD_GLOBAL
:加载全局变量的值STORE_FAST
:保存值到全局变量BUILD_LIST
:创建列表BUILD_MAP
:创建空字典STORE_NAME
:初始化字典内容
_code object_
>>> add.__code__
<code object add at 0x000001DF9F9EC030, file "<stdin>", line 1>
>>> list(add.__code__.co_code)
[100, 1, 125, 0, 100, 2, 125, 1, 124, 0, 124, 1, 23, 0, 83, 0]
>>> dis.opname[100]
'LOAD_CONST'
>>> dis.opname[125]
'STORE_FAST'
>>> add.__code__.co_consts
(None, 1, 2)
>>> add.__code__.co_varnames
('a', 'b')
__code__
是与函数对象关联的code object,co_code
是它的字节码(指令和指令参数的索引)co_varnames
是定义过的变量,co_consts
是给a,b
的赋值,
基本控制语句
顺序语句
2 0 LOAD_CONST 1 (1)
以第一行字节码为例,Python解释器解释这一行字节码,执行“从code object的co_consts
中取索引值为1的数值1存入数据栈中”这一操作,然后逐条执行之后的字节码指令。
条件语句
想要实现条件和循环结构,需要让解释器能够在指令中跳转。例如:
>>> import dis
>>> def condition() -> str:
... a = 1
... if a <= 3:
... return "<="
... else:
... return ">"
...
>>> dis.dis(condition)
2 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (a)
3 4 LOAD_FAST 0 (a)
6 LOAD_CONST 2 (3)
8 COMPARE_OP 1 (<=)
10 POP_JUMP_IF_FALSE 16
4 12 LOAD_CONST 3 ('<=')
14 RETURN_VALUE
6 >> 16 LOAD_CONST 4 ('>')
18 RETURN_VALUE
20 LOAD_CONST 0 (None)
22 RETURN_VALUE
>>> list(condition.__code__.co_code)
[100, 1, 125, 0, 124, 0, 100, 2, 107, 1, 114, 16, 100, 3, 83, 0, 100, 4, 83, 0, 100, 0, 83, 0]
源代码中的条件语句被编译成LOAD_FAST, LOAD_CONST, COMPARE_OP, POP_JUMP_IF_FALSE
,比较a和3之后,满足条件继续执行,如果不满足,POP_JUMP_IF_FALSE
让解释器跳转到索引为16的指令。
循环语句
与条件语句类似,循环语句也需要实现指令跳转。例如:
>>> import dis
>>> def loop() -> int:
... a = 1
... while a <= 3:
... a = a + 1
... return a
...
>>> dis.dis(loop)
2 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (a)
3 >> 4 LOAD_FAST 0 (a)
6 LOAD_CONST 2 (3)
8 COMPARE_OP 1 (<=)
10 POP_JUMP_IF_FALSE 22
4 12 LOAD_FAST 0 (a)
14 LOAD_CONST 1 (1)
16 BINARY_ADD
18 STORE_FAST 0 (a)
20 JUMP_ABSOLUTE 4
5 >> 22 LOAD_FAST 0 (a)
24 RETURN_VALUE
>>> list(loop.__code__.co_code)
[100, 1, 125, 0, 124, 0, 100, 2, 107, 1, 114, 22, 124, 0, 100, 1, 23, 0, 125, 0, 113, 4, 124, 0, 83, 0]
在这段字节码中,解释器先执行比较,满足条件继续执行,JUMP_ABSOLUTE
让解释器跳转到循环开始的位置,条件不满足时,POP_JUMP_IF_FALSE
让解释器跳转到索引值为22的指令结束循环。
栈
除了上文提到的存储变量值的数据栈之外还需要使用调用栈,Python程序的每个作用域都有一个帧,当函数被调用时,这个函数对应的帧会被压入调用栈,当这个函数执行完毕并返回时,这个帧就出栈(帧在Python代码执行过程中动态的创建和销毁)
另外,为了处理循环、异常处理等控制流块,还需要创建块栈。调用栈中的每一帧都有它的数据栈和块栈。