2016-03-06 54 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>