如何查看装饰函数的字节码?

How can I see the bytecode of a decorated function?

我想查看带有装饰器的装饰函数的字节码。

例如在下面的例子中,fibonacci 被 memoized 装饰。但是,当我在 fibonacci 上调用 'dis.dis' 时,这将显示实际函数的字节码。

我希望能够查看一个函数是否被装饰,并查看包含装饰部分的字节码。

我是不是完全误解了一些概念?

import collections
import functools

class memoized(object):
   '''Decorator. Caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned
   (not reevaluated).
   '''
   def __init__(self, func):
      self.func = func
      self.cache = {}

   def __call__(self, *args):
      if not isinstance(args, collections.Hashable):
         # uncacheable. a list, for instance.
         # better to not cache than blow up.
         return self.func(*args)
      if args in self.cache:
         print 'get cached version{}'.format(args)
         return self.cache[args]
      else:
         print 'compute {}'.format(args)
         value = self.func(*args)
         self.cache[args] = value
         return value

   def __repr__(self):
      '''Return the function's docstring.'''
      return self.func.__doc__

   def __get__(self, obj, objtype):
      '''Support instance methods.'''
      return functools.partial(self.__call__, obj)

@memoized
def fibonacci(n):
   "Return the nth fibonacci number."
   if n in (0, 1):
      return n
   return fibonacci(n-1) + fibonacci(n-2)

print fibonacci(12)

import dis
f = fibonacci
dis.dis(f)

您正在 实例 上调用 dis.dis()memoized 装饰器是 class,memoized(function) returns 是 class.

的一个实例

例如,instance.__dict__ 对象的值中的所有代码或函数对象都被反汇编(因为 dis() 函数假定它正在处理 class)。由于原始函数是一个代码对象,所以它被反汇编了。就好像你调用了 dis.dis(f.func);这就是 dis.dis() 输出以行 Disassembly of func.

开头的原因

如果您想显示 memoized.__call__ 方法的字节码,您必须在 memoized class 上调用 dis.dis()(并查看反汇编__init____call__),或者直接反汇编 memoized.__call__ 方法,方法是使用 dis.dis(memoized.__call__)dis.dis(fibonacci.__call__) 为反汇编程序提供对未绑定或绑定的引用方法。

由于装饰 只是语法糖 调用另一个对象传入一个函数,然后用结果替换那个函数,所以不存在装饰器的反汇编这样的事情与原来的功能结合在一起。你能做的最好的就是分别反汇编装饰器可调用函数和原始函数:

>>> dis.dis(fibonacci.__call__)
 15           0 LOAD_GLOBAL              0 (isinstance)
              3 LOAD_FAST                1 (args)
              6 LOAD_GLOBAL              1 (collections)
              9 LOAD_ATTR                2 (Hashable)
             12 CALL_FUNCTION            2
             15 POP_JUMP_IF_TRUE        31

 18          18 LOAD_FAST                0 (self)
             21 LOAD_ATTR                3 (func)
             24 LOAD_FAST                1 (args)
             27 CALL_FUNCTION_VAR        0
             30 RETURN_VALUE

 19     >>   31 LOAD_FAST                1 (args)
             34 LOAD_FAST                0 (self)
             37 LOAD_ATTR                4 (cache)
             40 COMPARE_OP               6 (in)
             43 POP_JUMP_IF_FALSE       71

 20          46 LOAD_CONST               1 ('get cached version{}')
             49 LOAD_ATTR                5 (format)
             52 LOAD_FAST                1 (args)
             55 CALL_FUNCTION            1
             58 PRINT_ITEM
             59 PRINT_NEWLINE

 21          60 LOAD_FAST                0 (self)
             63 LOAD_ATTR                4 (cache)
             66 LOAD_FAST                1 (args)
             69 BINARY_SUBSCR
             70 RETURN_VALUE

 23     >>   71 LOAD_CONST               2 ('compute {}')
             74 LOAD_ATTR                5 (format)
             77 LOAD_FAST                1 (args)
             80 CALL_FUNCTION            1
             83 PRINT_ITEM
             84 PRINT_NEWLINE

 24          85 LOAD_FAST                0 (self)
             88 LOAD_ATTR                3 (func)
             91 LOAD_FAST                1 (args)
             94 CALL_FUNCTION_VAR        0
             97 STORE_FAST               2 (value)

 25         100 LOAD_FAST                2 (value)
            103 LOAD_FAST                0 (self)
            106 LOAD_ATTR                4 (cache)
            109 LOAD_FAST                1 (args)
            112 STORE_SUBSCR

 26         113 LOAD_FAST                2 (value)
            116 RETURN_VALUE
            117 LOAD_CONST               0 (None)
            120 RETURN_VALUE
>>> dis.dis(fibonacci.func)
 39           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               4 ((0, 1))
              6 COMPARE_OP               6 (in)
              9 POP_JUMP_IF_FALSE       16

 40          12 LOAD_FAST                0 (n)
             15 RETURN_VALUE

 41     >>   16 LOAD_GLOBAL              0 (fibonacci)
             19 LOAD_FAST                0 (n)
             22 LOAD_CONST               2 (1)
             25 BINARY_SUBTRACT
             26 CALL_FUNCTION            1
             29 LOAD_GLOBAL              0 (fibonacci)
             32 LOAD_FAST                0 (n)
             35 LOAD_CONST               3 (2)
             38 BINARY_SUBTRACT
             39 CALL_FUNCTION            1
             42 BINARY_ADD
             43 RETURN_VALUE

您可以从 fibonacci.__call__ 反汇编中看到它会调用 self.func()(字节代码 18 到 27),这就是为什么您会查看 fibonacci.func.

对于使用闭包的 function 装饰器,您必须通过查看 __closure__ 对象进入包装闭包以提取原始函数:

>>> def memoized(func):
...    cache = {}
...    def wrapper(*args):
...       if not isinstance(args, collections.Hashable):
...          # uncacheable. a list, for instance.
...          # better to not cache than blow up.
...          return func(*args)
...       if args in cache:
...          print 'get cached version{}'.format(args)
...          return cache[args]
...       else:
...          print 'compute {}'.format(args)
...          value = func(*args)
...          cache[args] = value
...          return value
...    return wrapper
...
>>> @memoized
... def fibonacci(n):
...    "Return the nth fibonacci number."
...    if n in (0, 1):
...       return n
...    return fibonacci(n-1) + fibonacci(n-2)
...
>>> fibonacci.__closure__
(<cell at 0x1035ed590: dict object at 0x103606d70>, <cell at 0x1036002f0: function object at 0x1035fe9b0>)
>>> fibonacci.__closure__[1].cell_contents
<function fibonacci at 0x1035fe9b0>