假设我编写了一个装饰器,它执行了非常通用的操作。例如,它可能会将所有参数转换为特定类型,执行日志记录,实现备忘录等。
这是一个例子:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
>>> funny_function("3", 4.0, z="5")
22
到目前为止一切都很好。但是,有一个问题。装饰的函数不保留原始函数的文档:
>>> help(funny_function)
Help on function g in module __main__:
g(*args, **kwargs)
幸运的是,有一种解决方法:
def args_as_ints(f):
def g(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(*args, **kwargs)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
这次,函数名称和文档是正确的:
>>> help(funny_function)
Help on function funny_function in module __main__:
funny_function(*args, **kwargs)
Computes x*y + 2*z
但是仍然存在一个问题:函数签名是错误的。信息“ * args,** kwargs”几乎没有用。
该怎么办?我可以想到两个简单但有缺陷的解决方法:
1-在文档字符串中包含正确的签名:
def funny_function(x, y, z=3):
"""funny_function(x, y, z=3) -- computes x*y + 2*z"""
return x*y + 2*z
由于重复,这很糟糕。签名仍不会在自动生成的文档中正确显示。更新函数很容易,而不必更改文档字符串,也不会打错字。[
并且是的,我知道docstring已经复制了函数主体。 请忽略此;funny_function只是一个随机示例。]
2-请勿对每个特定签名使用装饰器,或使用专用装饰器:
def funny_functions_decorator(f):
def g(x, y, z=3):
return f(int(x), int(y), z=int(z))
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
这对于具有相同签名的一组函数很好用,但是通常没有用。正如我在一开始所说的那样,我希望能够完全通用地使用装饰器。
我正在寻找一种完全通用的自动解决方案。
所以问题是:创建修饰后的函数签名后,是否有办法对其进行编辑?
否则,我可以编写一个装饰器来提取函数签名并在构造装饰函数时使用该信息而不是“ * kwargs,**
kwargs”吗?如何提取该信息?我应该如何使用exec构造修饰的函数?
还有其他方法吗?
安装装饰器模块:
$ pip install decorator
修改以下内容的定义args_as_ints()
:
import decorator
@decorator.decorator
def args_as_ints(f, args, kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return f(args, **kwargs)
@args_as_ints
def funny_function(x, y, z=3):
“”“Computes xy + 2z”“”
return xy + 2z
print funny_function(“3”, 4.0, z=”5”)
help(funny_function)
functools.wraps()
自Python
3.4起,来自stdlib的文件就保留了签名:
import functools
def args_as_ints(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
args = [int(x) for x in args]
kwargs = dict((k, int(v)) for k, v in kwargs.items())
return func(*args, **kwargs)
return wrapper
@args_as_ints
def funny_function(x, y, z=3):
"""Computes x*y + 2*z"""
return x*y + 2*z
print(funny_function("3", 4.0, z="5"))
# 22
help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(x, y, z=3)
# Computes x*y + 2*z
functools.wraps()
至少从Python 2.5开始就可用,但是它不在那里保留签名:
help(funny_function)
# Help on function funny_function in module __main__:
#
# funny_function(*args, **kwargs)
# Computes x*y + 2*z
注意:*args, **kwargs
代替x, y, z=3
。
问题内容: 装饰器模式(功能)有很多好处: 当一个方法具有许多正交的关注点时,这将非常有用…也就是说,这些关注点均不相关,除了我们每次调用我们的方法时都希望全部(或部分)关注它们。这是装饰器模式真正有用的地方。 通过实现装饰器模式,我们订阅了开闭主体。我们的方法对将来的扩展开放,但对将来的修改不开放。遵循开放-封闭原则有很多有趣的好处。 但是,我发现的所有示例都非常复杂(例如,编写带有许多中间件的
问题内容: 我希望仅当登录用户具有所需的权限级别时,其他功能才可执行。 为了使我的生活更加复杂,我想使用装饰器。下面,我尝试在“装饰”功能上设置属性-如下所示。 但是当我这样做时: 我得到一个错误 我想念什么? 问题答案: 您正在检查内部(包装)函数上的属性,但在原始(包装)函数上进行了设置。但是,您 根本 需要包装函数: 你的装饰需要返回 的东西 那将取代原有的功能。原始函数本身(添加了属性)可
问题内容: 我的数据源提供了一个,但是对于我的ListView我需要一个。 A 基本上只是字符串的装饰器,添加了一个布尔值以提供一种跟踪ListView复选框状态的方法,如本答案所述。 目前,我正在观察原始列表中的更改事件,并手动在警告列表中添加/删除项目: 我的问题是: 有没有更优雅的方式来装饰我的String类型列表,因此可以将其用作Warning类型列表,而无需手动传递更改事件? 更准确地说
问题 你想通过反省或者重写类定义的某部分来修改它的行为,但是你又不希望使用继承或元类的方式。 解决方案 这种情况可能是类装饰器最好的使用场景了。例如,下面是一个重写了特殊方法 __getattribute__ 的类装饰器, 可以打印日志: def log_getattribute(cls): # Get the original implementation orig_getatt
问题 你写了一个装饰器作用在某个函数上,但是这个函数的重要的元信息比如名字、文档字符串、注解和参数签名都丢失了。 解决方案 任何时候你定义装饰器的时候,都应该使用 functools 库中的 @wraps 装饰器来注解底层包装函数。例如: import time from functools import wraps def timethis(func): ''' Decorato
我有一个项目,我想在链接阶段保留某些功能/部分。 我试过: 但这是针对编译器的,而不是针对链接器的。 现在链接器负责并使用了-gc部分,我想保留的功能“消失了”:( 如何指示链接器不删除所需的函数。 我想这样做的原因是函数的后期构建用法,通过改变二进制文件上的内容。