【问题标题】:Is there a way to make dis.dis() print code objects recursively?有没有办法让 dis.dis() 递归打印代码对象?
【发布时间】:2017-07-03 04:18:26
【问题描述】:

我一直在使用 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() 递归地打印代码对象?也就是说,如果我有嵌套的代码对象,它将打印出 all 的代码对象的字节码,而不是在顶级代码对象处停止。我主要喜欢这个特性,比如装饰器、闭包或生成器理解。

看来最新版本的 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 

What’s New In Python 3.7 文章记录了这一点:

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

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

【问题讨论】:

    标签: python recursion disassembly


    【解决方案1】:

    你可以这样做(Python 3):

    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__)
    

    【讨论】:

      【解决方案2】:

      首先,如果您需要它用于交互式使用以外的任何用途,我建议您只从 Python 3.7 源代码复制代码并向后移植它(希望这并不困难)。

      对于交互式使用,一个想法是使用access an object by its memory value 的一种方法通过其内存地址获取代码对象,该地址打印在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 行如果您向解释器传递内存中不存在的内容(例如,因为它已被垃圾回收),则会导致解释器出现段错误。来自@ 的其他一些解决方案987654322@ 可能会更好(我尝试了gc,但它似乎无法找到code 对象)。

      这也意味着如果你传递dis 一个字符串,这个不会工作,因为当你尝试访问内部代码对象时,它们已经被垃圾回收了。您需要将一个真正的 Python 对象传递给它,或者,如果您有一个字符串,则首先传递给它compile()

      【讨论】:

        猜你喜欢
        • 2019-07-04
        • 1970-01-01
        • 2010-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-29
        • 2020-03-19
        • 1970-01-01
        相关资源
        最近更新 更多