如何在Python中去除函数的装饰器
假设我有以下内容:
def with_connection(f):
def decorated(*args, **kwargs):
f(get_connection(...), *args, **kwargs)
return decorated
@with_connection
def spam(connection):
# Do something
我想测试一下 spam
这个函数,但不想麻烦地去设置连接(或者说这个装饰器在做什么)。
那么,给定 spam
,我该怎么去掉它的装饰器,得到一个没有装饰的“原始”函数呢?
10 个回答
34
balpha的解决方案可以通过这个元装饰器变得更通用:
def include_original(dec):
def meta_decorator(f):
decorated = dec(f)
decorated._original = f
return decorated
return meta_decorator
然后你可以用@include_original来装饰你的装饰器,这样每个装饰器内部都会有一个可以测试的(未装饰的)版本。
@include_original
def shout(f):
def _():
string = f()
return string.upper()
return _
@shout
def function():
return "hello world"
>>> print function()
HELLO_WORLD
>>> print function._original()
hello world
104
这个问题有了一些更新。如果你在使用Python 3,并且用了@functools.wraps
,那么你可以使用__wrapped__
这个属性来处理标准库中的装饰器。
这里有一个例子,来自《Python Cookbook》第三版,第9.3节 装饰器的解包
>>> @somedecorator
>>> def add(x, y):
... return x + y
...
>>> orig_add = add.__wrapped__
>>> orig_add(3, 4)
7
>>>
如果你想从自定义装饰器中解包一个函数,那么这个装饰器函数需要使用functools
中的wraps
函数。具体讨论可以参考《Python Cookbook》第三版,第9.2节 编写装饰器时保留函数元数据
>>> from functools import wraps
>>> def somedecorator(func):
... @wraps(func)
... def wrapper(*args, **kwargs):
... # decorator implementation here
... # ......
... return func(*args, **kwargs)
...
... return wrapper
57
一般情况下,你是做不到的,因为
@with_connection
def spam(connection):
# Do something
这相当于
def spam(connection):
# Do something
spam = with_connection(spam)
这意味着“原始”的垃圾邮件可能已经不存在了。一个(不是很优雅的)解决办法是:
def with_connection(f):
def decorated(*args, **kwargs):
f(get_connection(...), *args, **kwargs)
decorated._original = f
return decorated
@with_connection
def spam(connection):
# Do something
spam._original(testcon) # calls the undecorated function