在 Python 3.7 之前,有没有办法让 dis.dis() 递归地打印代码对象?

问题描述 投票:0回答:2

我一直在使用

dis
模块来观察 CPython 字节码。但最近,我注意到
dis.dis()
有一些不方便的行为。

以这个例子为例:我首先定义一个函数

multiplier
,里面有一个嵌套函数
inner
:

>>> def multiplier(n):
    def inner(multiplicand):
        return multiplicand * n
    return inner

>>> 

然后我用

dis.dis()
来拆解它:

>>> from dis import dis
>>> dis(multiplier)
  2           0 LOAD_CLOSURE             0 (n)
              3 BUILD_TUPLE              1
              6 LOAD_CONST               1 (<code object inner at 0x7ff6a31d84b0, file "<pyshell#12>", line 2>)
              9 LOAD_CONST               2 ('multiplier.<locals>.inner')
             12 MAKE_CLOSURE             0
             15 STORE_FAST               1 (inner)

  4          18 LOAD_FAST                1 (inner)
             21 RETURN_VALUE
>>>

如您所见,它很好地反汇编了顶级代码对象。不过,它并没有拆解

inner
。它只是表明它创建了一个名为
inner
的代码对象,并显示代码对象的默认(无信息)
__repr__()

有没有办法让

dis.dis()
递归地打印代码对象?也就是说,如果我有嵌套的代码对象,它将打印出所有代码对象的字节码,而不是在顶级代码对象处停止。我主要喜欢这个功能用于装饰器、闭包或生成器理解等。 看来最新版本的 Python -

3.7 alpha 1

- 完全具有我想要的行为 dis.dis():


>>> def func(a): def ifunc(b): return b + 10 return ifunc >>> dis(func) 2 0 LOAD_CONST 1 (<code object ifunc at 0x7f199855ac90, file "python", line 2>) 2 LOAD_CONST 2 ('func.<locals>.ifunc') 4 MAKE_FUNCTION 0 6 STORE_FAST 1 (ifunc) 4 8 LOAD_FAST 1 (ifunc) 10 RETURN_VALUE Disassembly of <code object ifunc at 0x7f199855ac90, file "python", line 2>: 3 0 LOAD_FAST 0 (b) 2 LOAD_CONST 1 (10) 4 BINARY_ADD 6 RETURN_VALUE

Python 3.7 的新增功能 文章记录了这一点:

dis()

函数现在能够反汇编嵌套代码对象(推导式、生成器表达式和嵌套函数的代码,以及用于构建嵌套类的代码)。 (由 Serhiy Storchaka 在 bpo-11822 中贡献。)



但是,除了Python 3.7还没有正式发布之外,如果你不想或者不能使用Python 3.7怎么办?有没有办法在早期版本的 Python(例如 3.5 或 2.7)中使用旧的
dis.dis()

来完成此任务?

    

python recursion disassembly
2个回答
2
投票

import dis def recursive_dis(code): print(code) dis.dis(code) for obj in code.co_consts: if isinstance(obj, type(code)): print() recursive_dis(obj)

https://repl.it/@solly_ucko/Recursive-dis

请注意,您必须使用

f.__code__

来调用它,而不仅仅是

f
。例如:

def multiplier(n): def inner(multiplicand): return multiplicand * n return inner recursive_dis(multiplier.__code__)



1
投票

对于交互式使用,一个想法是使用其中一种方法来

通过对象的内存值访问对象

来通过其内存地址获取代码对象,该地址打印在dis输出中。


例如:

>>> def func(a): ... def ifunc(b): ... return b + 10 ... return ifunc >>> import dis >>> dis.dis(func) 2 0 LOAD_CONST 1 (<code object ifunc at 0x10cabda50, file "<stdin>", line 2>) 3 LOAD_CONST 2 ('func.<locals>.ifunc') 6 MAKE_FUNCTION 0 9 STORE_FAST 1 (ifunc) 4 12 LOAD_FAST 1 (ifunc) 15 RETURN_VALUE

这里我复制粘贴上面打印的代码对象的内存地址

>>> import ctypes >>> c = ctypes.cast(0x10cabda50, ctypes.py_object).value >>> dis.dis(c) 3 0 LOAD_FAST 0 (b) 3 LOAD_CONST 1 (10) 6 BINARY_ADD 7 RETURN_VALUE

警告:如果您向解释器传递内存中不存在的内容(例如,因为它已被垃圾收集),则 

ctypes.cast 行将使解释器出现段错误。

 
上述引用的问题中的一些其他解决方案可能有效更好(我尝试了gc,但它似乎无法找到
code
对象)。

这也意味着,如果您传递

dis

一个字符串,则此不会工作,因为当您尝试访问内部代码对象时,它们已经被垃圾收集了。您需要向其传递一个真正的 Python 对象,或者,如果您有一个字符串,请先

compile()
它。
    

© www.soinside.com 2019 - 2024. All rights reserved.