您正在尝试反汇编包含源代码的字符串,但这不受支持dis.dis
在 Python 2 中。使用字符串参数时,它将字符串视为包含字节代码(请参阅函数disassemble_string in dis.py)。因此,您会看到基于将源代码误解为字节代码的无意义输出。
Python 3 中情况有所不同,其中dis.dis编译一个字符串参数拆解之前:
Python 3.2.3 (default, Aug 13 2012, 22:28:10)
>>> import dis
>>> dis.dis('heapq.nlargest(d,3)')
1 0 LOAD_NAME 0 (heapq)
3 LOAD_ATTR 1 (nlargest)
6 LOAD_NAME 2 (d)
9 LOAD_CONST 0 (3)
12 CALL_FUNCTION 2
15 RETURN_VALUE
在 Python 2 中,您需要先自己编译代码,然后再将其传递给dis.dis
:
Python 2.7.3 (default, Aug 13 2012, 18:25:43)
>>> import dis
>>> dis.dis(compile('heapq.nlargest(d,3)', '<none>', 'eval'))
1 0 LOAD_NAME 0 (heapq)
3 LOAD_ATTR 1 (nlargest)
6 LOAD_NAME 2 (d)
9 LOAD_CONST 0 (3)
12 CALL_FUNCTION 2
15 RETURN_VALUE
这些数字代表着什么?号码1
最左边是编译该字节代码的源代码中的行号。左边一列的数字是指令在字节码中的偏移量,右边的数字是指令在字节码中的偏移量opargs。让我们看看实际的字节码:
>>> co = compile('heapq.nlargest(d,3)', '<none>', 'eval')
>>> co.co_code.encode('hex')
'6500006a010065020064000083020053'
在字节码的偏移量 0 处我们发现65
,操作码为LOAD_NAME
,与 oparg0000
;然后(在偏移量 3 处)6a
是操作码LOAD_ATTR
, with 0100
oparg 等等。请注意,opargs 是小端顺序,因此0100
是数字 1。无证opcode
模块包含表格opname
为您提供每个操作码的名称,以及opmap
为您提供每个名称的操作码:
>>> opcode.opname[0x65]
'LOAD_NAME'
oparg 的含义取决于操作码,要了解完整的故事,您需要阅读 CPython 虚拟机的实现in ceval.c. For LOAD_NAME
and LOAD_ATTR
oparg 是一个索引co_names
代码对象的属性:
>>> co.co_names
('heapq', 'nlargest', 'd')
For LOAD_CONST
它是一个索引co_consts
代码对象的属性:
>>> co.co_consts
(3,)
For CALL_FUNCTION
,它是传递给函数的参数数量,以 16 位编码,低字节为普通参数数量,高字节为关键字参数数量。