问题
给定以下代码(一个任意的装饰器,它只是将数据分配给一些可调用的(
from typing import Callable
def decorator() -> Callable:
def wrapper(f: Callable) -> Callable:
f.foo = "bar"
return f
return wrapper
@decorator()
def func(a: int, b: int) -> int:
return a + b
如果我尝试以下操作,我会得到预期的行为。
>>> func.foo
"bar"
>>> func(1,2)
3
然而,如果我想使用functools.partial
,则f.foo = "bar"
关系是"0">断裂";
>>> from functools import partial
>>> p = partial(func, a=1)
>>> p(b=2)
3
# As expected, p.foo raises an attribute error
>>> p.foo
AttributeError: 'partial' object has no attribute 'foo'
我意识到我可以通过p.func.foo
访问它。但我很好奇,当使用分部时,是否有更好的方法来重建关系。
尝试的解决方案
下面的变通方法似乎是">工作";,但我很好奇是否有更好的方法。重写__new__
很奇怪,所以我选择重写__init__
class new_partial(partial):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for k, v in self.func.__dict__.items():
setattr(self, k, v)
鉴于上述情况,以下内容现在适用于
>>> p = new_partial(func, a=1)
>>> p(b=2)
3
>>> p.foo
"bar"
如果你提前计划,你可以单独装饰函数的部分版本:
def func(a: int, b: int) -> int:
return a + b
p = decorator()(partial(func, 1))
func = decorator()(func)
如果你不介意单独的p.foo
和func.foo
的存在,你也可以只装饰部分。
对于创建新函数(而不是在原始函数上设置属性(的装饰器,可以使用functools.wraps
,以便函数在__wrapped__
属性中记住其原始函数,并对其进行局部和重新装饰:
from functools import partial, wraps
def get_name(func):
try:
return func.__name__
except AttributeError: # handle a partial
return 'a partial application of ' + get_name(func.func)
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"this is a decorated version of {get_name(func)}")
return func(*args, **kwargs)
return wrapper
@decorator
def decorated_sum(x, y):
return x + y
increment = decorator(partial(decorated_sum.__wrapped__, 1))
您甚至可以创建一个助手函数来查找__wrapped__
属性,但您需要构建自己的方法来确定要应用哪些装饰器。