免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

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

Python面向对象:__add__、__eq__等运算符重载方法 Python面向对象add、__eq__等运算符重载方法一、开篇让、对你的对象也有效当你写3 5时Python实际调用的是(3).__add__(5)。当你写hello world时调用的是hello.__eq__(world)。Python的每一个运算符背后都有一个魔法方法——你可以为自己的类实现这些方法让、-、、这些运算符对你的对象生效。⌨️ 先看最直观的例子classVector:二维向量——支持加减def__init__(self,x,y):self.xx self.yydef__add__(self,other):v1 v2returnVector(self.xother.x,self.yother.y)def__sub__(self,other):v1 - v2returnVector(self.x-other.x,self.y-other.y)def__repr__(self):returnfVector({self.x},{self.y})v1Vector(3,4)v2Vector(1,2)print(v1v2)# Vector(4, 6)print(v1-v2)# Vector(2, 2)二、算术运算符2.1 双目运算符classMoney:金额类——支持算术运算def__init__(self,amount,currencyCNY):self.amountamount self.currencycurrencydef__add__(self,other):ifself.currency!other.currency:raiseValueError(不同货币不能直接相加)returnMoney(self.amountother.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):金额 ÷ 数量ifdivisor0:raiseZeroDivisionError(除数不能为0)returnMoney(self.amount/divisor,self.currency)def__floordiv__(self,divisor):金额 // 数量returnMoney(self.amount//divisor,self.currency)def__repr__(self):returnfMoney({self.amount:.2f},{self.currency})# 使用priceMoney(100)quantity3totalprice*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.yx,ydef__add__(self,other):returnVector(self.xother.x,self.yother.y)def__radd__(self,other):反向加法当左操作数不支持加法时调用# 例如tuple Vectorifisinstance(other,(tuple,list))andlen(other)2:returnVector(self.xother[0],self.yother[1])returnNotImplementeddef__iadd__(self,other):增强赋值 self.xother.x self.yother.yreturnself# 必须返回selfdef__repr__(self):returnfVector({self.x},{self.y})vVector(3,4)# __radd__的例子# result (1, 2) v # 元组不支持 VectorPython调用v.__radd__((1,2))# print(result) # Vector(4, 6)# __iadd__的例子vVector(1,1)print(v)# Vector(4, 5) —— v自身被修改了三、比较运算符3.1 等于和不等于classPerson:def__init__(self,name,id_number):self.namename self.id_numberid_numberdef__eq__(self,other): —— 通过身份证号判断是否同一个人ifnotisinstance(other,Person):returnNotImplementedreturnself.id_numberother.id_numberdef__ne__(self,other):! —— 通常直接反转__eq__即可resultself.__eq__(other)ifresultisNotImplemented:returnresultreturnnotresultdef__hash__(self):定义了__eq__就必须定义__hash__用于set和dict的键returnhash(self.id_number)def__repr__(self):returnfPerson({self.name},{self.id_number})p1Person(张三,110101199001011234)p2Person(张三别名,110101199001011234)# 同一身份证号p3Person(李四,110101199501011234)print(p1p2)# True —— 同一个人print(p1p3)# Falseprint(p1!p3)# True# 可以放入集合——根据__hash__和__eq__去重people{p1,p2,p3}print(len(people))# 2 —— p1和p2被视为同一个3.2 大小比较fromfunctoolsimporttotal_orderingtotal_ordering# 只需定义__eq__和__lt__其余自动生成classVersion:语义化版本号def__init__(self,major,minor,patch):self.majormajor self.minorminor self.patchpatchdef__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):returnfv{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、lenclassMatrix:简单的二维矩阵——支持[][]索引def__init__(self,rows,cols,default0):self.rowsrows self.colscols 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)# 使用mMatrix(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)# True4.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)五、总结运算符重载让你的对象用起来像内置类型一样自然。但不要滥用——只在你真的需要、且行为符合直觉时才重载。核心要点运算符背后是魔法方法——→__add__→__eq____eq__和__hash__要一起定义——否则set/dict出问题total_ordering——定义__eq__和__lt__就自动生成全部比较方法反向运算用__radd__等——当左操作数不支持时被调用不支持的操作返回NotImplemented——让Python尝试对方的反向方法✅原则运算符的行为应该符合直觉。永远是加法或拼接永远是比较相等——不要搞创新。
返回列表