除非您使用某些特殊的实现(例如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) 10 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_FAST0从而从保存参数的任何地方获取参数0(
n传递给
fib),并将其压入解释器的堆栈(Python的解释器是堆栈计算机)。在阅读下一条说明时,
LOAD_CONST1,它将从该函数拥有的常量集合中获取一个常量1(在这种情况下恰好是2),并将其压入堆栈。您实际上可以看到以下常量:
>>> fib.func_pre.co_consts(None, 2, 1)
下一条指令,
COMPARE_OP0告诉解释器弹出两个最顶部的堆栈元素,并在它们之间进行不等式比较,将布尔结果推回堆栈。第四条指令基于布尔值确定是向前跳五条指令还是继续下一条指令。所有这些动词都解释
ifn <2了条件表达式中的部分
fib。弄清楚其余
fib字节码的含义和行为,对您而言将是非常有启发性的练习。唯一的一个,我不知道的是
POP_TOP,我猜想
JUMP_IF_FALSE已定义为将其布尔参数保留在堆栈上而不是将其弹出,因此必须显式弹出它。
更具指导意义的是检查原始字节码,
fib从而:
>>> pre = fib.func_pre.co_pre>>> pre'|x00x00dx01x00jx00x00ox05x00x01|x00x00Sx01tx00x00|x00x00dx01x00x18x83x01x00tx00x00|x00x00dx02x00x18x83x01x00x17S'>>> import oppre>>> op = pre[0]>>> op'|'>>> op = ord(op)>>> op124>>> oppre.opname[op]'LOAD_FAST'>>>
因此,您可以看到字节码的第一个字节是
LOAD_FAST指令。下一对字节
'x00x00'(16位中的数字0)是的参数
LOAD_FAST,并告诉字节码解释器将参数0加载到堆栈上。



