python - Python:如何装饰一个特殊的(dunder)方法
问题描述
包装一个特殊的方法是可行的,但对实例的行为没有预期的效果。
例如,如果我调用,装饰a.__call__
方法(实例 a)确实会生效a.__call__(x)
,但如果我调用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__
10,使用input_wrap_decorator
. 这是发生的事情:
>>> 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
,因此它不会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)
推荐阅读
- php - 从wordpress中的作者搜索中获取所有帖子
- java - Spring Data Rest Put 未在 SpringBoot 2.1.x 上使用不可变实体进行更新
- powerbi - 根据不同计数计算价格
- mongodb - 数组中的匹配数组过滤器未提供所需结果
- command-line - 从“p4 add”添加的文件未显示在 p4v 默认更改列表中
- winapi - 如何在 MFC 中增加 ComboBox 项的长度
- javascript - 如何在html页面报告上显示每个屏幕的百分比计算
- android - 没有为模型配置 python 解释器
- python - 在 Travis CI 中运行测试用例时无法获取环境变量
- java - 使用字符串进行字母数字验证的 Java 程序没有给我想要的输出