如何从 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 ,我如何从中剥离装饰器并获得底层的“未装饰”功能?

原文由 Herge 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 423
2 个回答

在一般情况下,你不能,因为

@with_connection
def spam(connection):
    # Do something

相当于

def spam(connection):
    # Do something

spam = with_connection(spam)

这意味着“原始”垃圾邮件甚至可能不再存在。一个(不太漂亮的)hack 是这样的:

 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

原文由 balpha 发布,翻译遵循 CC BY-SA 2.5 许可协议

这个问题有一些更新。如果您使用的是 Python 3,并且 @functools.wraps 您可以使用 __wrapped__ stdlib 装饰器的属性。

这是 Python Cookbook 第 3 版第 9.3 节解包装饰器中 的示例

>>> @somedecorator
>>> def add(x, y):
...     return x + y
...
>>> orig_add = add.__wrapped__
>>> orig_add(3, 4)
7
>>>

如果您尝试从自定义装饰器中解包函数,则装饰器函数需要使用 wraps 来自 functools 请参阅 Python Cookbook,第 3 版,第 9.2 节编写时保留函数元数据 中的讨论 装饰者

>>> from functools import wraps
>>> def somedecorator(func):
...    @wraps(func)
...    def wrapper(*args, **kwargs):
...       # decorator implementation here
...       # ......
...       return func(*args, **kwargs)
...
...    return wrapper

原文由 Alex Volkov 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题