我希望看到装饰函数的字节码及其装饰器。
例如,在下面的示例中,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)
答案 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__