理解 Python 字节码
admin
2023-07-31 01:42:49
0

我最近在参与Python字节码相关的工作,想与大家分享一些这方面的经验。更准确的说,我正在参与2.6到2.7版本的CPython解释器字节码的工作。

Python是一门动态语言,在命令行工具下运行时,本质上执行了下面的步骤:

  • 当第一次执行到一段代码时,这段代码会被编译(如,作为一个模块加载,或者直接执行)。根据操作系统的不同,这一步生成后缀名是pyc或者pyo的二进制文件。
  • 解释器读取二进制文件,并依次执行指令(opcodes)。

Python解释器是基于栈的。要理解数据流向,我们需要知道每条指令的栈效应(如,操作码和参数)。

探索Python二进制文件

得到一个二进制文件字节码的最简单方式,是对CodeType结构进行解码:

123456 ; htmlscript: false ]import marshalfd = open(\’path/to/my.pyc\’, \’rb\’)magic = fd.read(4) # 魔术数,与python版本相关date = fd.read(4) # 编译日期code_object = marshal.load(fd)fd.close()

code_object包含了一个CodeType对象,它代表被加载文件的整个模块。为了查看这个模块的类定义、方法等的所有嵌套编码对象(编码对象,原文为code object),我们需要递归地检查CodeType的常量池。就像下面的代码:

123456789 ; htmlscript: false ]import types def inspect_code_object(co_obj, indent=\’\’):print indent, \”%s(lineno:%d)\” % (co_obj.co_name, co_obj.co_firstlineno)for c in co_obj.co_consts:if isinstance(c, types.CodeType):inspect_code_object(c, indent + \’ \’) inspect_code_object(code_object) # 从第一个对象开始

这个案例中,我们打印出一颗编码对象树,每个编码对象是其父对象的子节点。对下面的代码:

1234567 ; htmlscript: false ]class A:def __init__(self):passdef __repr__(self):return \’A()\’a = A()print a

我们得到的树形结果是:

1234 <module>(lineno:2)   A(lineno:2)     __init__(lineno:3)     __repr__(lineno:5)

为了测试,我们可以通过compile指令,编译一个包含Python源码的字符串,从而能够得到一个编码对象:

1 co_obj = compile(python_source_code, \'\’, \’exec\’)

要获取更多关于编码对象的信息,我们可以查阅Python文档的co_* fields 部分。

初见字节码

一旦我们得到了编码对象,我们就可以开始对它进行拆解了(在co_code字段)。从字节码中解析出它的含义:
• 解释操作码的含义
• 提取任意参数

dis模块的disassemble函数展示了是如何做到的。对我们前面例子,它输出的结果是:

1234567891011121314151617 2   0 LOAD_CONST        0 (\’A\’)    3 LOAD_CONST        3 (())    6 LOAD_CONST        1 (<code object A at 0x42424242, file \”\”, line 2>)    9 MAKE_FUNCTION     0   12 CALL_FUNCTION     0   15f976565833-5\”>   12 CALL_FUNCTION     0   15/div>

我最近在参与Python字节码相关的工作,想与大家分享一些这方面的经验。更准确的说,我正在参与2.6到2.7版本的CPython解释器字节码的工作。

Python是一门动态语言,在命令行工具下运行时,本质上执行了下面的步骤:

  • 当第一次执行到一段代码时,这段代码会被编译(如,作为一个模块加载,或者直接执行)。根据操作系统的不同,这一步生成后缀名是pyc或者pyo的二进制文件。
  • 解释器读取二进制文件,并依次执行指令(opcodes)。

Python解释器是基于栈的。要理解数据流向,我们需要知道每条指令的栈效应(如,操作码和参数)。

探索Python二进制文件

得到一个二进制文件字节码的最简单方式,是对CodeType结构进行解码:

123456 ; htmlscript: false ]import marshalfd = open(\’path/to/my.pyc\’, \’rb\’)magic = fd.read(4) # 魔术数,与python版本相关date = fd.read(4) # 编译日期code_object = marshal.load(fd)fd.close()

code_object包含了一个CodeType对象,它代表被加载文件的整个模块。为了查看这个模块的类定义、方法等的所有嵌套编码对象(编码对象,原文为code object),我们需要递归地检查CodeType的常量池。就像下面的代码:

123456789 ; htmlscript: false ]import types def inspect_code_object(co_obj, indent=\’\’):print indent, \”%s(lineno:%d)\” % (co_obj.co_name, co_obj.co_firstlineno)for c in co_obj.co_consts:if isinstance(c, types.CodeType):inspect_code_object(c, indent + \’ \’) inspect_code_object(code_object) # 从第一个对象开始

这个案例中,我们打印出一颗编码对象树,每个编码对象是其父对象的子节点。对下面的代码:

1234567 ; htmlscript: false ]class A:def __init__(self):passdef __repr__(self):return \’A()\’a = A()print a

我们得到的树形结果是:

1234 <module>(lineno:2)   A(lineno:2)     __init__(lineno:3)     __repr__(lineno:5)

为了测试,我们可以通过compile指令,编译一个包含Python源码的字符串,从而能够得到一个编码对象:

1 co_obj = compile(python_source_code, \'\’, \’exec\’)

要获取更多关于编码对象的信息,我们可以查阅Python文档的co_* fields 部分。

初见字节码

一旦我们得到了编码对象,我们就可以开始对它进行拆解了(在co_code字段)。从字节码中解析出它的含义:
• 解释操作码的含义
• 提取任意参数

dis模块的disassemble函数展示了是如何做到的。对我们前面例子,它输出的结果是:

1234567891011121314151617 2   0 LOAD_CONST        0 (\’A\’)    3 LOAD_CONST        3 (())    6 LOAD_CONST        1 (<code object A at 0x42424242, file \”\”, line 2>)    9 MAKE_FUNCTION     0   12 CALL_FUNCTION     0   15on-h\”>

相关内容

热门资讯

Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...
python清除字符串里非数字... 本文实例讲述了python清除字符串里非数字字符的方法。分享给大家供大家参考。具体如下: impor...