如何在python中“事后”将装饰器附加到函数上?

Posted

技术标签:

【中文标题】如何在python中“事后”将装饰器附加到函数上?【英文标题】:How can one attach a decorator to a function "after the fact" in python? 【发布时间】:2016-02-25 10:22:06 【问题描述】:

我理解python中函数装饰器的方式(我可能错了)是它们应该添加副作用并修改函数的返回值。现在装饰器被添加到要装饰的函数的函数定义之上或通过赋值。这是一个小例子:

def print_args_decor(function):
    def wrapper(*args, **kwargs):
        print 'Arguments:', args, kwargs         # Added side-effect
        return function(*args, **kwargs)*5       # Modified return value
    return wrapper

@print_args_decor
def do_stuff(strg, n=10):
    """Repeats strg a few times."""
    return strg * n

new_decorated_func = print_args_decor(do_stuff)  # Decoration by assignment

print do_stuff('a', 2) # Output: aaaaaaaaaa

现在,如何将装饰器附加到其他地方定义的函数,理想情况下保留原始函数的名称和文档字符串(就像functools.wraps 那样)?例如,我从 Python 的数学模块中导入 sqrt() 函数,并想装饰它,我该怎么做?

from functools import wraps
from math import sqrt

def print_args_decor(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        print 'Arguments:', args, kwargs         # Added side-effect
        return function(*args, **kwargs)*5       # Modified return value
    return wrapper

# Decorate the sqrt() function from math module somehow
@print_args_decor #???
sqrt #???

print sqrt(9)   
# Output: 
# Arguments: ([9],) 
# 15                   # <--- sqrt(9)*5

事后装饰类中的方法怎么样?自己装饰班级怎么样?

【问题讨论】:

【参考方案1】:

您将sqrt 导入到您的模块中,只需在您自己的全局命名空间中应用装饰器即可:

sqrt = print_args_decor(sqrt)

这会将模块命名空间中的名称 sqrt 设置为装饰器的结果。不要求 sqrt 最初在此模块中定义。

由装饰器决定是否使用functools.wraps() 装饰器来保存函数元数据,例如名称和文档字符串。

装饰一个类在这方面没有什么不同:

ClassName = decorator(ClassName)

在 Python 2 上,对于 methods 需要小心抓取原始的未绑定函数;最简单的方法是使用method.__func__ 属性:

try:
    # Python 2
    ClassName.function_name = decorator(ClassName.function_name.__func__)
except AttributeError:
    # Python 3
    ClassName.function_name = decorator(ClassName.function_name)

我已将上述内容封装在 try...except 中,以使该模式适用于 Python 版本。另一种方法是从__dict__ 类中获取函数对象,以避免描述符协议启动:

ClassName.function_name = decorator(ClassName.__dict__['function_name'])

【讨论】:

看起来很简单,谢谢。你能添加一个在类中装饰类和方法的例子吗? @con-f-use:装饰器只是一个可调用对象。正如您已经说过的,@decorator 语法只是将装饰对象传递给可调用的装饰器,并用结果替换名称。所以对于一个只有ClassName = decorator(ClassName)的类。 @con-f-use:现有方法有点棘手,因为装饰器在 函数对象 成为类的一部分之前就应用于它。可以通过__func__属性获取原函数:ClassName.function_name = decorator(ClassName.function_name.__func__)

以上是关于如何在python中“事后”将装饰器附加到函数上?的主要内容,如果未能解决你的问题,请参考以下文章

将装饰器附加到类中的所有函数

Python 初始—(装饰器)

python 装饰器从放弃到玩转(初级)

装饰者模式

python之装饰器

使用 Python 即时存储数据