如何在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中“事后”将装饰器附加到函数上?的主要内容,如果未能解决你的问题,请参考以下文章