dis.dis() 默认不显示隐式 return none,可用 show_caches=true(3.12+)或显式添加 return none 对比字节码;循环/异常结构需结合 setup_loop 和异常表分析;跨版本差异大,须统一小版本号;c 扩展函数无法 dis。

dis.dis() 看不见 return None 怎么办
Python 函数末尾没写 return,解释器会自动补 return None,但 dis.dis() 默认不显示这行隐式指令——容易误判函数是否真有返回值。
实际调试时,如果发现字节码里没看到 RETURN_VALUE,别急着认为函数没返回,先确认是不是被优化掉了。CPython 在某些简单函数中会省略显式 LOAD_CONST None + RETURN_VALUE,直接用单条 RETURN_VALUE 结束(此时栈顶恰好是 None)。
- 用
dis.dis(func, show_caches=True)(Python 3.12+)可查看更完整的控制流,包括隐式返回点 - 对老版本,加一句显式
return None再反汇编,对比字节码差异,能快速定位“消失的返回” - 注意:
@property或__init__方法即使没写return,其字节码也一定以RETURN_VALUE结尾,这是语言规范要求
dis.get_instructions() 解析循环和异常块太乱
dis.get_instructions() 返回的是扁平指令流,但 for、try 这类结构在字节码里靠跳转地址(POP_JUMP_IF_FALSE、JUMP_ABSOLUTE)和异常表(except 块起始/结束偏移)组织,纯看指令列表根本看不出嵌套关系。
常见错误是把 JUMP_BACKWARD 当成普通跳转,结果误判循环体范围;或忽略异常表里的 start/end 字段,把 except 块的清理逻辑当成主流程。
立即学习“Python免费学习笔记(深入)”;
天天企业网站管理系统简繁英三语版(TianTian CMS)是由天天网络科技工作室开发的多语言企业网站源码,主要功能模块有企业信息、新闻动态、产品展示、资源下载、视频中心、人才招聘、支持服务、会员中心、留言反馈等。会员可用QQ快速登录。可在线订购产品和实时支付。运行环境:ASP+ACCESS(或ms sql),采用DIV+CSS构架,使网页整洁美观。代码用UTF-8编码,通用性比较好,适合国内外服
- 用
dis.show_code(func)先看整体结构,重点关注ExceptionTable:部分,它比指令流更能反映真实控制流 - 处理循环时,优先找
SETUP_LOOP指令(Python 3.11+ 是ENTER_EXECUTOR),它的target就是循环体起始,配合后续的跳转指令才能圈出完整范围 - 不要依赖指令序号做逻辑判断——字节码优化(如常量折叠)会让序号错位,始终以
offset和跳转目标为准
不同 Python 版本 dis 输出差异大到没法比
Python 3.11 引入了自适应字节码(per-opcode specialization),3.12 又重写了异常表格式,同一段代码在不同版本下 dis.dis() 输出可能差出一倍指令数,比如 CALL 被拆成 CALL_FUNCTION_EX + CALL_METHOD 专用指令。
这意味着你在一个版本里看到的优化痕迹(比如 LOAD_FAST 替代 LOAD_GLOBAL),换到另一个版本可能根本不存在,甚至指令名都变了。
- 查版本用
sys.version_info,不是python --version——后者可能指向不同解释器 - 跨版本对比时,先统一用
dis.Bytecode(func).codeobj.co_code拿原始字节,再人工映射(Python 3.11+ 的co_linetable已废弃,改用co_lines()) - 生产环境排查性能问题,务必用和线上一致的小版本号(如 3.11.8 而非笼统的 3.11.x),patch 版本差异也会导致指令行为变化
想用 dis 分析 C 扩展模块的 PyCFunction?行不通
dis.dis() 只能处理 Python 编译后的 code 对象,而 PyCFunction(比如 len()、list.append())压根没有字节码——它们是直接调用 C 函数指针,dis 会报 TypeError: don't know how to disassemble builtin_function_or_method objects。
有人试图用 ctypes 读取 C 函数地址再反汇编机器码,这不仅跨平台失败率高,还绕过了 Python 的抽象层,看到的只是 ABI 调用约定,和实际语义无关。
- 确认对象类型:用
isinstance(obj, types.BuiltinFunctionType)或检查obj.__code__是否存在 - 替代方案是看 CPython 源码对应函数(如
Objects/listobject.c里的list_append),比硬反汇编靠谱得多 - 若必须观测调用开销,用
perf record -e cycles,instructions python script.py配合perf report,而不是在dis上死磕
字节码不是源码快照,它是解释器执行时的中间契约——同一段 Python 代码,在不同优化等级、不同版本、不同上下文(比如是否在 eval() 中)下,生成的字节码都可能不同。盯住 offset 和跳转目标,比记指令名重要得多。









