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

时间:2016-03-06 15:55:48

标签: python bytecode disassembly

我希望看到装饰函数的字节码及其装饰器。

例如,在下面的示例中,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)

1 个答案:

答案 0 :(得分:1)

您正在实例上调用dis.dis() ; memoized装饰器是一个类,memoized(function)返回该类的实例。

对于实例,instance.__dict__对象的值中的所有代码或函数对象都被反汇编(因为dis()函数假定它正在处理类)。由于原始函数是代码对象,因此它被反汇编。就像你打电话给dis.dis(f.func);这就是dis.dis()输出以行Disassembly of func开头的原因。

如果您想显示memoized.__call__方法的字节码,则必须在dis.dis()类上调用memoized(并查看{{1}的反汇编和__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__(字节代码18到27),这就是您查看self.func()的原因。

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

__closure__