2016-03-06 52 views
0

我想看看装饰器的装饰函数的字节码。如何查看装饰函数的字节码?

例如在下面的例子中,fibonacci是通过memoized装饰的。但是,当我在斐波那契上调用“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

你是一个实例调用dis.dis();装饰器是memoized类,并且memoized(function)返回该类的一个实例。

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

如果你想显示memoized.__call__方法的字节码,你必须要么在memoized类调​​用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

对于功能使用封闭装饰,你不得不通过看__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>