当前位置: 代码网 > it编程>前端脚本>Python > Python面向对象之__add__、__eq__运算符重载方法详解

Python面向对象之__add__、__eq__运算符重载方法详解

2026年09月15日 Python 我要评论
一、开篇:让+、==对你的对象也有效当你写3 + 5时,python实际调用的是(3).__add__(5)。当你写"hello" == "world"时,调用

一、开篇:让+、==对你的对象也有效

当你写3 + 5时,python实际调用的是(3).__add__(5)。当你写"hello" == "world"时,调用的是"hello".__eq__("world")。python的每一个运算符背后都有一个魔法方法——你可以为自己的类实现这些方法,让+-==<这些运算符对你的对象生效。

先看最直观的例子:

class vector:
    """二维向量——支持加减"""
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        """v1 + v2"""
        return vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        """v1 - v2"""
        return vector(self.x - other.x, self.y - other.y)

    def __repr__(self):
        return f"vector({self.x}, {self.y})"

v1 = vector(3, 4)
v2 = vector(1, 2)
print(v1 + v2)  # vector(4, 6)
print(v1 - v2)  # vector(2, 2)

二、算术运算符

2.1 双目运算符

class money:
    """金额类——支持算术运算"""
    def __init__(self, amount, currency="cny"):
        self.amount = amount
        self.currency = currency

    def __add__(self, other):
        if self.currency != other.currency:
            raise valueerror("不同货币不能直接相加")
        return money(self.amount + other.amount, self.currency)

    def __sub__(self, other):
        if self.currency != other.currency:
            raise valueerror("不同货币不能直接相减")
        return money(self.amount - other.amount, self.currency)

    def __mul__(self, factor):
        """金额 × 数量"""
        if isinstance(factor, (int, float)):
            return money(self.amount * factor, self.currency)
        return notimplemented  # 不支持的操作返回notimplemented

    def __truediv__(self, divisor):
        """金额 ÷ 数量"""
        if divisor == 0:
            raise zerodivisionerror("除数不能为0")
        return money(self.amount / divisor, self.currency)

    def __floordiv__(self, divisor):
        """金额 // 数量"""
        return money(self.amount // divisor, self.currency)

    def __repr__(self):
        return f"money({self.amount:.2f}, {self.currency})"

# 使用
price = money(100)
quantity = 3
total = price * quantity
print(total)        # money(300.00, cny)
print(total / 2)    # money(150.00, cny)
print(money(500) - money(200))  # money(300.00, cny)

2.2 反向运算符和增强赋值

class vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __add__(self, other):
        return vector(self.x + other.x, self.y + other.y)

    def __radd__(self, other):
        """反向加法:当左操作数不支持加法时调用"""
        # 例如:tuple + vector
        if isinstance(other, (tuple, list)) and len(other) == 2:
            return vector(self.x + other[0], self.y + other[1])
        return notimplemented

    def __iadd__(self, other):
        """增强赋值 += """
        self.x += other.x
        self.y += other.y
        return self  # 必须返回self!

    def __repr__(self):
        return f"vector({self.x}, {self.y})"

v = vector(3, 4)
# __radd__的例子
# result = (1, 2) + v  # 元组不支持 + vector,python调用v.__radd__((1,2))
# print(result)  # vector(4, 6)

# __iadd__的例子
v += vector(1, 1)
print(v)  # vector(4, 5) —— v自身被修改了

三、比较运算符

3.1 等于和不等于

class person:
    def __init__(self, name, id_number):
        self.name = name
        self.id_number = id_number

    def __eq__(self, other):
        """== —— 通过身份证号判断是否同一个人"""
        if not isinstance(other, person):
            return notimplemented
        return self.id_number == other.id_number

    def __ne__(self, other):
        """!= —— 通常直接反转__eq__即可"""
        result = self.__eq__(other)
        if result is notimplemented:
            return result
        return not result

    def __hash__(self):
        """定义了__eq__就必须定义__hash__(用于set和dict的键)"""
        return hash(self.id_number)

    def __repr__(self):
        return f"person({self.name}, {self.id_number})"

p1 = person("张三", "110101199001011234")
p2 = person("张三别名", "110101199001011234")  # 同一身份证号
p3 = person("李四", "110101199501011234")

print(p1 == p2)  # true —— 同一个人!
print(p1 == p3)  # false
print(p1 != p3)  # true

# 可以放入集合——根据__hash__和__eq__去重
people = {p1, p2, p3}
print(len(people))  # 2 —— p1和p2被视为同一个

3.2 大小比较

from functools import total_ordering

@total_ordering  # 只需定义__eq__和__lt__,其余自动生成
class version:
    """语义化版本号"""
    def __init__(self, major, minor, patch):
        self.major = major
        self.minor = minor
        self.patch = patch

    def __eq__(self, other):
        return (self.major, self.minor, self.patch) == \
               (other.major, other.minor, other.patch)

    def __lt__(self, other):
        return (self.major, self.minor, self.patch) < \
               (other.major, other.minor, other.patch)

    def __repr__(self):
        return f"v{self.major}.{self.minor}.{self.patch}"

versions = [version(2, 0, 1), version(1, 5, 3),
            version(2, 1, 0), version(1, 0, 0)]
print(sorted(versions))
# [v1.0.0, v1.5.3, v2.0.1, v2.1.0]

# @total_ordering自动生成了:__le__, __gt__, __ge__

四、容器类运算符

4.1getitem、setitem、len

class matrix:
    """简单的二维矩阵——支持[][]索引"""
    def __init__(self, rows, cols, default=0):
        self.rows = rows
        self.cols = cols
        self._data = [[default] * cols for _ in range(rows)]

    def __getitem__(self, index):
        """matrix[row][col]"""
        return self._data[index]

    def __setitem__(self, index, value):
        """matrix[row] = [val1, val2, ...]"""
        if len(value) != self.cols:
            raise valueerror(f"每行必须有{self.cols}个元素")
        self._data[index] = list(value)

    def __len__(self):
        """len(matrix) → 行数"""
        return self.rows

    def __contains__(self, value):
        """value in matrix —— 检查矩阵中是否有某个值"""
        return any(value in row for row in self._data)

    def __repr__(self):
        return "\n".join(" ".join(f"{v:3d}" for v in row) for row in self._data)

# 使用
m = matrix(3, 4)
m[0] = [1, 2, 3, 4]
m[1] = [5, 6, 7, 8]
m[2] = [9, 10, 11, 12]
print(m[0][2])     # 3
print(f"行数: {len(m)}")  # 3
print(7 in m)      # true

4.2 完整运算符速查表

# 算术运算符
# __add__(+), __sub__(-), __mul__(*), __truediv__(/), __floordiv__(//)
# __mod__(%), __pow__(**), __divmod__()
# __radd__, __rsub__, ... (反向运算符)
# __iadd__, __isub__, ... (增强赋值 += 等)

# 比较运算符
# __eq__(==), __ne__(!=), __lt__(<), __le__(<=), __gt__(>), __ge__(>=)

# 一元运算符
# __neg__(-), __pos__(+), __abs__(abs()), __invert__(~)

# 容器运算符
# __len__(len), __getitem__([]), __setitem__([]=), __delitem__(del [])
# __contains__(in), __iter__(for)

# 类型转换
# __int__(int), __float__(float), __bool__(bool), __str__(str), __bytes__(bytes)

# 其他
# __call__(obj()), __enter__/__exit__(with), __hash__(hash)

五、总结

运算符重载让你的对象用起来像内置类型一样自然。但不要滥用——只在你真的需要、且行为符合直觉时才重载。

核心要点:

  1. 运算符背后是魔法方法——+__add__==__eq__
  2. __eq____hash__要一起定义——否则set/dict出问题
  3. @total_ordering——定义__eq____lt__就自动生成全部比较方法
  4. 反向运算用__radd__等——当左操作数不支持时被调用
  5. 不支持的操作返回notimplemented——让python尝试对方的反向方法

原则:运算符的行为应该"符合直觉"。+永远是加法或拼接,==永远是比较相等——不要搞创新。

到此这篇关于python面向对象之__add__、__eq__运算符重载方法详解的文章就介绍到这了,更多相关python运算符重载内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com