您正在尝试拆解内含源代码的字符串,但是这不支持
dis.dis在Python
2.用字符串参数,它把字符串,如果它包含字节码(见函数
disassemble_string中
dis.py)。因此,您会看到基于将源代码误解为字节码而产生的无意义的输出。
Python 3中的情况有所不同,Python
3在反汇编之前先
dis.dis编译字符串参数:
Python 3.2.3 (default, Aug 13 2012, 22:28:10) >>> import dis>>> dis.dis('heapq.nlargest(d,3)') 10 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')) 10 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_pre.enpre('hex')'6500006a010065020064000083020053'我们在字节码的偏移量0处找到了oparg
65的操作码; 那么(在偏移量3处)是操作码,操作参数,以此类推。请注意,opargs的顺序为little-
endian,即数字1。未记录的模块包含一些表,这些表为您提供每个操作码的名称,并为您提供每个名称的操作码:
LOAD_NAME``0000``6a``LOAD_ATTR``0100``0100``oppre``opname``opmap
>>> oppre.opname[0x65]'LOAD_NAME'
oparg的含义取决于操作码,对于全文,您需要阅读中的CPython虚拟机的实现
ceval.c。For
LOAD_NAME和
LOAD_ATTRoparg是
co_names代码对象属性的索引:
>>> co.co_names('heapq', 'nlargest', 'd')因为
LOAD_CONST它是
co_consts代码对象属性的索引:
>>> co.co_consts(3,)
对于
CALL_FUNCTION,它是传递给函数的参数数目,以16位编码,低字节为普通参数的数目,高字节为关键字参数的数目。



