Python解释器 – 执行Python程序最后的一步
在解释器执行程序之前,Python会执行其他三个步骤:词法分析、语法分析与语义分析、编译。这三步将Python源代码转换为code object,解释器的工作就是解释执行code object中的指令(字节码)。
- Python解释器是一个虚拟机,模拟真实计算机的软件(模拟CPU主要功能)
- CPU主要功能功能:取指,译码,执行
- code object:Python源代码转换为字节码(指令的集合)存放在其中,还会存有常量、变量、位置参数、关键字参数等信息
- 词法分析,语法分析和语义分析,编译:有对应的词法分析器,语法分析器,编译器来一起生成code object
编写一代解释器
我们给出的指令不是真正的字节码,是用dis库反汇编出来的,真正的字节码是二进制的给电脑看的
我们从一个小的解释器开始,计算两个数的合。只能理解三个指令:
LOAD_CONST:将co_consts元组对应下标的数压栈
BINARY_ADD:将栈顶元素与下一个元素弹出,进行加法,得到的结果重新压栈
RETURN_VALUE:将栈顶元素弹出并作为返回值返回
现阶段指令如何产生我们不关心,假设编译器已经生成指令,我们来研究指令如何执行
一个指令与内存中的一条二进制一样,分为两个部分,操作码、与操作数。操作码为该指令要做什么如加法、减法。操作码为需要的数据去哪里找。
所以生成的指令集包含字节码与需要用到的数据
假设7+5生成如下指令
what_to_execute = {
"instructions": [("LOAD_CONST", 0), # the first number
("LOAD_CONST", 1), # the second number
("BINARY_ADD", None),
("RETURN_VALUE", None)],
"co_consts": (7, 5) }
注意:为什么不讲数值嵌入指令中呢:如果我们加的不是数字,而是字符串。而且这种设计也意味着我们只需要对象的一份拷贝,比如这个加法 7 + 7
, 现在常量表 "co_consts"
只需包含一个7
Python的解释器是一个基于栈的解释器,执行所有的指令,数据的交互都是通过操作栈来完成的,栈的特点就是先进后出,可以用Python的列表来表示。
class Interpreter:
def __init__(self):
self.stack = []
def LOAD_CONST(self, number):
# LOAD_CONST:将co_consts对应下标值压栈
self.stack.append(number)
def RETURN_VALUE(self):
# RETURN_VALUE:将栈顶元素弹出,将返回值返回
answer = self.stack.pop()
print(answer)
def BINARY_ADD(self):
# 将栈顶元素与下一个元素弹出,进行相加,结果压栈
first_number = self.stack.pop()
second_number = self.stack.pop()
result = first_number + second_number
self.stack.append(result)
完成了解释器所理解的指令,还差最重要的一步,一个能把所有指令和数据结合在一起并执行的方法。一个循环,遍历指令,来完成执行。
def run_code(self, par_what_to_execute):
# 获取指令
instructions = par_what_to_execute["instructions"]
# 获取指令所需数据
numbers = par_what_to_execute["co_consts"]
for each_step in instructions:
instruction, argument = each_step
if instruction == "LOAD_CONST":
number = numbers[argument]
self.LOAD_CONST(number)
elif instruction == "BINARY_ADD":
self.BINARY_ADD()
elif instruction == "RETURN_VALUE":
self.RETURN_VALUE()
完成了,我们创建一个解释器对象,然后用前面定义的 7 + 5 的指令集来调用run_code
。
interpreter = Interpreter()
interpreter.run_code(what_to_execute)
# 输出为 12
我们上面写的这个解释器只要给出合适的指令集,不需要改变任何代码,就可以执行多个数的加法。可以试一试 10 + 13 + 20