如何避免手动定义Python类的所有dunder方法?
简化Sqrt类算术魔法方法的实现方案
不用手动逐个编写所有算术魔法方法,有几种简洁的批量实现方式:
1. 动态生成算术方法
可以写一个辅助函数,遍历需要支持的算术操作,自动为Sqrt类生成对应的魔法方法。这种方式能彻底消除代码重复,逻辑清晰且易于维护。
示例代码:
import math def generate_arith_methods(cls): # 定义需要支持的算术操作:(魔法方法名, 对应的运算逻辑) arith_ops = [ ('__add__', lambda a, b: a + b), ('__sub__', lambda a, b: a - b), ('__mul__', lambda a, b: a * b), ('__truediv__', lambda a, b: a / b), ('__floordiv__', lambda a, b: a // b), ('__mod__', lambda a, b: a % b), ('__pow__', lambda a, b: a ** b), # 反向操作(处理如 int + Sqrt 这类左操作数不是Sqrt实例的场景) ('__radd__', lambda a, b: b + a), ('__rsub__', lambda a, b: b - a), ('__rmul__', lambda a, b: b * a), ('__rtruediv__', lambda a, b: b / a), ('__rfloordiv__', lambda a, b: b // a), ('__rmod__', lambda a, b: b % a), ('__rpow__', lambda a, b: b ** a), ] for method_name, op_func in arith_ops: def create_method(op): def method(self, other): # 统一处理操作数:如果是Sqrt实例则取val属性,否则直接用原值 other_val = other.val if isinstance(other, Sqrt) else other result = op(self.val, other_val) # 这里可根据需求调整返回类型:如需保持Sqrt实例则返回新对象,否则直接返回数值 return Sqrt(sqr_val=result**2, val=result) return method setattr(cls, method_name, create_method(op_func)) return cls @generate_arith_methods class Sqrt: def __init__(self, sqr_val): self.sqr_val = sqr_val self.val = math.sqrt(sqr_val) def __repr__(self): return f"Sqrt(sqr_val={self.sqr_val}, val={self.val:.4f})"
只需在装饰器的操作列表里维护需要支持的运算,所有对应魔法方法都会自动生成。如果要修改运算后的返回逻辑(比如返回普通float而非Sqrt实例),直接调整return语句即可。
2. 利用__getattr__处理反向操作
对于正向操作(如sqrt_obj + 5)需要定义基础魔法方法,但反向操作(如5 + sqrt_obj)可以通过__getattr__动态处理,减少一半的代码量。这种方式适合只需要部分反向操作的场景。
示例代码:
import math class Sqrt: def __init__(self, sqr_val): self.sqr_val = sqr_val self.val = math.sqrt(sqr_val) # 正向算术方法示例 def __add__(self, other): other_val = other.val if isinstance(other, Sqrt) else other result = self.val + other_val return Sqrt(result**2) def __sub__(self, other): other_val = other.val if isinstance(other, Sqrt) else other result = self.val - other_val return Sqrt(result**2) # 其他正向方法可按同理补充... # 动态处理反向操作 def __getattr__(self, name): # 识别反向算术方法前缀 if name.startswith('__r') and name[3:] in ['add', 'sub', 'mul', 'truediv']: op_name = f'__{name[3:]}__' def reversed_method(other): # 调用左操作数的正向方法,交换操作数顺序 return getattr(other, op_name)(self) return reversed_method raise AttributeError(f"Sqrt object has no attribute '{name}'")
为什么继承int/float行不通?
int/float是不可变的内置类型,它们的构造逻辑和Sqrt类不兼容——你需要额外存储sqr_val属性,但继承内置类型后,实例的核心值是固定的,无法同时维护val和sqr_val两个独立属性,反而会让代码更复杂冗余。
总结:第一种动态生成方法的方案最彻底,能完全避免重复代码,是最推荐的实现方式。
内容的提问来源于stack exchange,提问作者Codeman
相关产品推荐
相关产品推荐

