You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免手动定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 15:32:03