Python面向对象:__add__、__eq__等运算符重载方法
2026/9/6 8:17:52 网站建设 项目流程

Python面向对象:add、__eq__等运算符重载方法

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

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

⌨️ 先看最直观的例子:

classVector:"""二维向量——支持加减"""def__init__(self,x,y):self.x=x self.y=ydef__add__(self,other):"""v1 + v2"""returnVector(self.x+other.x,self.y+other.y)def__sub__(self,other):"""v1 - v2"""returnVector(self.x-other.x,self.y-other.y)def__repr__(self):returnf"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 双目运算符

classMoney:"""金额类——支持算术运算"""def__init__(self,amount,currency="CNY"):self.amount=amount self.currency=currencydef__add__(self,other):ifself.currency!=other.currency:raiseValueError("不同货币不能直接相加")returnMoney(self.amount+other.amount,self.currency)def__sub__(self,other):ifself.currency!=other.currency:raiseValueError("不同货币不能直接相减")returnMoney(self.amount-other.amount,self.currency)def__mul__(self,factor):"""金额 × 数量"""ifisinstance(factor,(int,float)):returnMoney(self.amount*factor,self.currency)returnNotImplemented# 不支持的操作返回NotImplementeddef__truediv__(self,divisor):"""金额 ÷ 数量"""ifdivisor==0:raiseZeroDivisionError("除数不能为0")returnMoney(self.amount/divisor,self.currency)def__floordiv__(self,divisor):"""金额 // 数量"""returnMoney(self.amount//divisor,self.currency)def__repr__(self):returnf"Money({self.amount:.2f},{self.currency})"# 使用price=Money(100)quantity=3total=price*quantityprint(total)# Money(300.00, CNY)print(total/2)# Money(150.00, CNY)print(Money(500)-Money(200))# Money(300.00, CNY)

2.2 反向运算符和增强赋值

classVector:def__init__(self,x,y):self.x,self.y=x,ydef__add__(self,other):returnVector(self.x+other.x,self.y+other.y)def__radd__(self,other):"""反向加法:当左操作数不支持加法时调用"""# 例如:tuple + Vectorifisinstance(other,(tuple,list))andlen(other)==2:returnVector(self.x+other[0],self.y+other[1])returnNotImplementeddef__iadd__(self,other):"""增强赋值 += """self.x+=other.x self.y+=other.yreturnself# 必须返回self!def__repr__(self):returnf"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 等于和不等于

classPerson:def__init__(self,name,id_number):self.name=name self.id_number=id_numberdef__eq__(self,other):"""== —— 通过身份证号判断是否同一个人"""ifnotisinstance(other,Person):returnNotImplementedreturnself.id_number==other.id_numberdef__ne__(self,other):"""!= —— 通常直接反转__eq__即可"""result=self.__eq__(other)ifresultisNotImplemented:returnresultreturnnotresultdef__hash__(self):"""定义了__eq__就必须定义__hash__(用于set和dict的键)"""returnhash(self.id_number)def__repr__(self):returnf"Person({self.name},{self.id_number})"p1=Person("张三","110101199001011234")p2=Person("张三别名","110101199001011234")# 同一身份证号p3=Person("李四","110101199501011234")print(p1==p2)# True —— 同一个人!print(p1==p3)# Falseprint(p1!=p3)# True# 可以放入集合——根据__hash__和__eq__去重people={p1,p2,p3}print(len(people))# 2 —— p1和p2被视为同一个

3.2 大小比较

fromfunctoolsimporttotal_ordering@total_ordering# 只需定义__eq__和__lt__,其余自动生成classVersion:"""语义化版本号"""def__init__(self,major,minor,patch):self.major=major self.minor=minor self.patch=patchdef__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):returnf"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.1getitemsetitemlen

classMatrix:"""简单的二维矩阵——支持[][]索引"""def__init__(self,rows,cols,default=0):self.rows=rows self.cols=cols self._data=[[default]*colsfor_inrange(rows)]def__getitem__(self,index):"""matrix[row][col]"""returnself._data[index]def__setitem__(self,index,value):"""matrix[row] = [val1, val2, ...]"""iflen(value)!=self.cols:raiseValueError(f"每行必须有{self.cols}个元素")self._data[index]=list(value)def__len__(self):"""len(matrix) → 行数"""returnself.rowsdef__contains__(self,value):"""value in matrix —— 检查矩阵中是否有某个值"""returnany(valueinrowforrowinself._data)def__repr__(self):return"\n".join(" ".join(f"{v:3d}"forvinrow)forrowinself._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])# 3print(f"行数:{len(m)}")# 3print(7inm)# 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尝试对方的反向方法

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询