如何将装饰器附加到 python 中的函数 "after the fact"?

How can one attach a decorator to a function "after the fact" in python?

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

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

事后在 类 中装饰方法怎么样?装饰 类 自己怎么样?

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

sqrt = print_args_decor(sqrt)

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

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

装饰 class 在这方面没有什么不同:

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 个版本。另一种方法是从 class __dict__ 中获取函数对象以避免描述符协议启动:

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