如何查看装饰函数的字节码?
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>
我想查看带有装饰器的装饰函数的字节码。
例如在下面的例子中,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>