python 内核分析(九):Pyc与字节码

本系列作为本人@takooctopus深入学习python机制的记录,这个博客遵照着栖迟于一丘的博客上面的流程进行的,也包含我在实际查看源码时的感想,特此列出,表示感谢。

Python字节码

我们知道Python虚拟机的实现机制是基于栈结构的,Cpython会使用三种类型的栈:

  • 调用栈call stack。这是运行 Python 程序的主要结构。它为每个当前活动的函数调用使用了一个东西 —— “帧frame”,栈底是程序的入口点。每个函数调用推送一个新的帧到调用栈,每当函数调用返回后,这个帧被销毁。

  • 在每个帧中,有一个计算栈evaluation stack (也称为数据栈data stack)。这个栈就是 Python 函数运行的地方,运行的 Python 代码大多数是由推入到这个栈中的东西组成的,操作它们,然后在返回后销毁它们。

  • 在每个帧中,还有一个块栈block stack。它被 Python 用于去跟踪某些类型的控制结构:循环、try / except 块、以及 with 块,全部推入到块栈中,当你退出这些控制结构时,块栈被销毁。这将帮助 Python 了解任意给定时刻哪个块是活动的,比如,一个 continue 或者 break 语句可能影响正确的块。

我们在[Include/opcode.h]中能找到关于这些字节码的定义:


{{- code -}}

上面就是定义的几乎全部操作字节码了。

因为有些指令需要参数,所以定义了HAVE_ARGUMENT这个作为分界,没有参数的指令在它上面,编码小于它「90」,有参数的指令编码大于「90」


{{- code -}}

而任何代码域对象在函数中可以以属性__code__ 来访问其PyCodeObject中的属性。

Pyc文件的反编译

系统自带的函数 dis.dis() 将反汇编一个函数、方法、类、模块、编译过的 Python 代码对象、或者字符串包含的源代码,以及显示出一个人类可读的版本。

举例来说


{{- code -}}

{{- code -}}

话说Python3.6之后,每条字节码不管有没有参数都占两字节,其中字节码指令和参数各占一字节了。