包装特殊方法有效,但对实例的行为没有预期的效果。
例如,如果我调用a.__call__
,那么装饰a.__call__(x)
方法(实例a)确实会生效,但如果我调用a(x)
则不会。
考虑以下函数,使装饰器预处理输入:
def input_wrap_decorator(preprocess):
def decorator(func):
def func_wrapper(*args, **kwargs):
return func(preprocess(*args, **kwargs))
return func_wrapper
return decorator
考虑这个简单的类:
class A:
def __call__(self, k):
return "{}({})".format(self.__class__.__name__, k)
演示其惊人的功能:
>>> a = A()
>>> a(7)
'A(7)'
现在说我想做一些关键的事情:使用__call__
将input_wrap_decorator
的所有输入乘以10。这是发生的事情:
>>> a = A()
>>> a.__call__ = input_wrap_decorator(preprocess=lambda x: x * 10)(a.__call__)
>>> a.__call__(7) # __call__ works as expected
'A(70)'
>>> a(7) # but a(.) does not!
'A(7)'
只有蟒蛇成年人才会知道......
对于自定义类,只有在对象的类型上定义,而不是在对象的实例字典中,才能保证特殊方法的隐式调用正常工作
所以,你可以这样做:
def input_wrap_decorator(preprocess):
def decorator(func):
def func_wrapper(self, *args, **kwargs):
return func(self, preprocess(*args, **kwargs))
return func_wrapper
return decorator
class A:
def __call__(self, k):
return "{}({})".format(self.__class__.__name__, k)
a = A()
# A.__call__ will be used by a(7), not a.__call__
A.__call__ = input_wrap_decorator(preprocess=lambda x: x * 10)(A.__call__)
print(a.__call__(7))
# A(70)
print(a(7))
# A(70)
请注意,我在self
中隔离了func_wrapper
,因此它不会与其他args一起传递给preprocess
。
当然,您可以将语法糖用于装饰器:
class A:
@input_wrap_decorator(preprocess=lambda x: x * 10)
def __call__(self, k):
return "{}({})".format(self.__class__.__name__, k)
a = A()
print(a.__call__(7))
# A(70)
print(a(7))
# A(70)