dis.dis()
模块中的dis
函数允许将原始字节码从代码对象反汇编为人类可读的形式。 dis()
的文档说明了可以传递给函数的内容:
反汇编
bytesource
个对象。bytesource
可以表示模块,类,方法,函数或代码对象。对于模块,它会反汇编所有功能。对于一个类,它会反汇编所有方法。对于单个代码序列,它每字节码指令打印一行。如果没有提供对象,它会反汇编最后一个回溯。
在试验模块时,一切都按预期工作。但是当我将一个str
对象传递给函数并且运行正常时,我感到很惊讶:
>>> import dis
>>> dis.dis('1 + 2')
0 <49>
1 SLICE+2
2 STORE_SLICE+3
3 SLICE+2
4 DELETE_SLICE+0
>>>
dis()
的文档具体表示允许传递给该函数的内容。我最后一次检查时,str
不满足任何这些要求。 Per the documentation for code objects:
代码对象表示字节编译的可执行Python代码或字节码。 [...]
但让我感到惊讶的是dis()
生成的字节码。这个字节码是什么意思?我决定通过查看dis
文档页面上的 32.12.1. Python Bytecode Instructions 来检查并查看每个操作码的含义。然而这让我更加困惑。有些操作码甚至没有记录(<49>
)。
Python尝试使用我传入的字符串究竟是什么。是否考虑将我的字符串作为文字源代码?或者是试图从我传入的字符串构造一个字符串?
由于looking over the source code for dis.disassemble_string()
(dis.dis()
传入str
对象时>>> def func():
... 1 + 2
...
>>> dis.dis(func)
2 0 LOAD_CONST 3 (3)
3 POP_TOP
4 LOAD_CONST 0 (None)
7 RETURN_VALUE
>>>
调用的内容),我认为我以前的猜测是正确的。但如果这是真的,那么为什么字节码看起来如此奇怪?如果我传入一个具有相同表达式的函数,则字节码非常有意义:
>>> dis.dis('foo = 10')
0 BUILD_TUPLE 28527
3 SLICE+2
4 DELETE_SUBSCR
5 SLICE+2
6 <49>
7 <48>
>>> dis.dis('print 0')
0 JUMP_IF_TRUE_OR_POP 26994
3 JUMP_FORWARD 8308 (to 8314)
6 <48>
>>> # etc...
但是,这种行为似乎并不局限于表达式。我尝试了其他几个语句,它们都生成了同样奇怪的字节码:
dis
这种行为是否记录在某处?我查看了ssh
模块的整个文档页面,但我没有找到任何相关信息。