Python 3.11中CodeType构造为何会修改输入的co_code?
Python 3.11中CodeType构造函数修改co_code的原因解析
问题场景
在Python 3.11中手动构造修改了co_code的代码对象时,发现types.CodeType构造函数会修改传入的co_code参数,最终生成的代码对象的co_code与输入不一致。示例代码及输出如下:
示例代码
import types import random def f(a): a = a + 1 return a codeobj = f.__code__ new_co_code = bytes([random.randint(0, 255) for _ in range(24)]) new_code = types.CodeType( codeobj.co_argcount, codeobj.co_posonlyargcount, codeobj.co_kwonlyargcount, codeobj.co_nlocals, codeobj.co_stacksize, codeobj.co_flags, new_co_code, codeobj.co_consts, codeobj.co_names, codeobj.co_varnames, codeobj.co_filename, codeobj.co_name, codeobj.co_qualname, codeobj.co_firstlineno, codeobj.co_lnotab, codeobj.co_exceptiontable, ) print(new_co_code) print(new_code.co_code) # 该断言通常会失败 assert new_co_code == new_code.co_code
示例输出
b'\xe6\x8f\xdb\xc8\xb8\xb86\xe6&a\x18\xfb\x11&\xbc\xce\xc0A\xedjg\x8a\xa4G' b'\x00\x8f\x00\xc8\x00\xb86\xe6\x8ca\xab\xfb\x00\x00\x00\x00\x00\x00\x00\x00g\x8a\xa4G'
原因解析
这种修改行为源于Python 3.11引入的**自适应解释器(Specializing Adaptive Interpreter)**机制,构造CodeType对象时,解释器会对传入的字节码做预处理优化,具体包括:
- 操作数窄化:Python 3.11调整了部分字节码的操作数长度,将原本2字节的宽操作数指令转换为1字节的窄操作数版本,减少内存占用并提升执行效率。
- 无效字节填充:对于不符合字节码格式规范的输入(比如随机生成的无效字节),解释器会用
0x00填充对齐,避免后续执行时出现更严重的错误。 - 性能监控补丁:自适应解释器需要在字节码中插入一些用于监控执行情况的标记,这也会修改原始字节码。
即使传入的字节码是无效的(比如示例中的随机字节),解释器仍会执行这些预处理步骤,目的是让字节码尽可能符合自适应解释器的执行规范,哪怕最终执行该代码对象会触发段错误。
相关参考
这部分属于CPython的内部实现细节,官方公开文档中没有详细说明,但可以通过以下途径了解:
- 查看CPython源代码中
Objects/codeobject.c文件的PyCode_NewWithPosOnlyArgs函数,其中包含字节码预处理的逻辑。 - PEP 659《Specializing Adaptive Interpreter》介绍了自适应解释器的整体设计,可作为背景知识参考。
内容的提问来源于stack exchange,提问作者Abhishek G
相关产品推荐
相关产品推荐

