我想打印
my_func
的源代码,它由 my_decorator
: 包裹
import inspect
from functools import wraps
def my_decorator(some_function):
@wraps(some_function)
def wrapper():
some_function()
return wrapper
@my_decorator
def my_func():
print "supposed to return this instead!"
return
print inspect.getsource(my_func)
但是,它返回包装器的源代码:
@wraps(some_function)
def wrapper():
some_function()
有没有办法让它打印以下内容?
def my_func():
print "supposed to return this instead!"
return
请注意,上面是从一个更大的程序中抽象出来的。当然,我们可以在这个例子中去掉装饰器,但这不是我想要的。
在 Python 2 中,
@functools.wraps()
装饰器不会设置 Python 3 版本添加的便利
__wrapped__
属性(Python 3.2 中的新功能)。
这意味着您必须从闭包中提取原始函数。确切的位置取决于装饰器的具体实现,但选择第一个函数对象应该是一个很好的概括:
from types import FunctionType
def extract_wrapped(decorated):
closure = (c.cell_contents for c in decorated.__closure__)
return next((c for c in closure if isinstance(c, FunctionType)), None)
用途:
print inspect.getsource(extract_wrapped(my_func))
使用您的示例进行演示:
>>> print inspect.getsource(extract_wrapped(my_func))
@my_decorator
def my_func():
print "supposed to return this instead!"
return
另一个选择是更新
functools
库来为您添加
__wrapped__
属性,与 Python 3 的方式相同:
import functools
def add_wrapped(uw):
@functools.wraps(uw)
def update_wrapper(wrapper, wrapped, **kwargs):
wrapper = uw(wrapper, wrapped, **kwargs)
wrapper.__wrapped__ = wrapped
return wrapper
functools.update_wrapper = add_wrapped(functools.update_wrapper)
在
导入您希望看到受影响的装饰器之前运行该代码(因此它们最终会使用新版本的functools.update_wrapper()
)。
您仍然必须手动解开(Python 2
inspect
模块不会去寻找属性);这是一个简单的辅助函数来做到这一点:def unwrap(func):
while hasattr(func, '__wrapped__'):
func = func.__wrapped__
return func
这将打开任何级别的装饰器包装。或者使用 Python 3中的
实现的副本,其中包括检查意外的循环引用。
@functool.wraps()
装饰器没有定义
__wrapped__
属性,这将使做你想做的事情变得非常容易。根据我读到的文档,尽管它是在 Python 3.2 中添加的,但在 3.4 版本发布之前,有时处理它的方式存在一个 bug,因此下面的代码使用 v3.4 作为截止日期用于定义自定义
wraps()
装饰器。从它的名字来看,听起来你可以控制
my_decorator()
,你可以通过定义你自己的
wraps
类函数来解决这个问题,而不是从闭包中提取原始函数,如他的答案所示。以下是具体操作方法(适用于 Python 2 和 3):(正如 Martijn 还指出的那样,您可以通过覆盖
functools.wraps
模块属性来对更改进行猴子修补,这将使更改也会影响使用
functools
的其他模块,而不仅仅是定义它的模块。)
import functools
import inspect
import sys
if sys.version_info[0:2] >= (3, 4): # Python v3.4+?
wraps = functools.wraps # built-in has __wrapped__ attribute
else:
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
updated=functools.WRAPPER_UPDATES):
def wrapper(f):
f = functools.wraps(wrapped, assigned, updated)(f)
f.__wrapped__ = wrapped # set attribute missing in earlier versions
return f
return wrapper
def my_decorator(some_function):
@wraps(some_function)
def wrapper():
some_function()
return wrapper
@my_decorator
def my_func():
print("supposed to return this instead!")
return
print(inspect.getsource(my_func.__wrapped__))
输出:
@my_decorator
def my_func():
print("supposed to return this instead!")
return
def extract_wrapped(decorated):
closure = decorated.__closure__
if closure:
for cell in closure:
if isinstance(cell.cell_contents, FunctionType) and cell.cell_contents.__closure__ is None:
return cell.cell_contents
elif isinstance(cell.cell_contents, FunctionType):
return extract_wrapped(cell.cell_contents)
return decorated
可以使用extract_wrapped函数来获取被修饰后的原始函数。例如:
import inspect
from types import FunctionType
def decorator(func):
def wrapper():
print("before")
func()
print("after")
return wrapper
@decorator
@decorator
def my_function():
print("func")
print("Decorated:")
my_function()
# before
# before
# func
# after
# after
def extract_wrapped(decorated):
closure = decorated.__closure__
if closure:
for cell in closure:
if isinstance(cell.cell_contents, FunctionType) and cell.cell_contents.__closure__ is None:
return cell.cell_contents
elif isinstance(cell.cell_contents, FunctionType):
return extract_wrapped(cell.cell_contents)
return decorated
original = extract_wrapped(my_function)
print("\nOriginal source:")
print(inspect.getsource(original))
# @decorator
# @decorator
# def my_function():
# print("func")
print("\nOriginal:")
original()
# func