找回密码
 会员注册
查看: 20|回复: 0

Python妙用运算符重载——玩出“点”花样来

[复制链接]

2万

主题

0

回帖

7万

积分

超级版主

积分
72021
发表于 2024-9-7 12:59:52 | 显示全部楼层 |阅读模式
目录运算符重载主角点类魔法方法__getitem____setitem____iter____next____len____neg____pos____abs____bool____call__重载运算符比较运算符相等==不等!=大于和小于>、=、>算术运算符加+减-乘*除/幂**取模 %整除// 右加+右幂**右整除//总结本篇的主角正是“点”,今天要用运算符重载来,把它玩出“点”花样来!那什么是运算符重载呢?运算符重载运算符重载是面向对象编程中的一个概念,它允许程序员为自定义类型(如类或结构体)定义特定的运算符行为,使得这些类的实例可以使用语言中预定义的运算符。在Python等编程语言中,运算符重载是一种强大的特性,它使得我们可以用更加自然和直观的方式处理自定义类型。在实际编程中,我们应该根据需要合理使用这一特性,以提高代码的质量和效率。主角点类classPoint这个类很简单,就两个属性:横坐标x和纵坐标y。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__str__(self):returnf'({self.x},{self.y})''运行运行测试:>>>a=Point()>>>aPoint(0,0)>>>str(a)'(0,0)'>>>b=Point(2,5)>>>bPoint(2,5)对于只需要整数坐标的类,比如二维数组的行列坐标,本文主要讨论整数坐标值的坐标,可以在类初始化函数里加上类型判断:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,yassert(isinstance(x,str)andisinstance(y,str))def__repr__(self):returnf'Point({self.x},{self.y})'def__str__(self):returnf'({self.x},{self.y})''运行运行测试: >>>p=Point(2,5)>>>pPoint(2,5)>>>q=Point(2.1,5.5)Traceback(mostrecentcalllast): File" ",line1,in  q=Point(2.1,5.5) File" ",line4,in__init__  assert(isinstance(x,int)andisinstance(y,int))AssertionError魔法方法也称为特殊方法或双下划线方法,是python语言中的一种特殊方法,用于在类中实现一些特殊的功能。这些方法的名称始终以双下划线开头和结尾,比如上面点类定义时用到 __init__,__repr__,__str__。重载运算符时,我们就是靠魔法方法来重新定义运算符的,例如__add__,__sub__,__mul__,__truediv__分别对应加减乘除四则运算。在重载运算符前,再来学习几个其他类型的魔法方法:__getitem____getitem__方法用于获取下标对应的值。__setitem____setitem__方法用于设置下标对应的值。定义完后,点类可以用下标0,1或者-2,-1来取值,和元组、列表等一样:obj[0],obj[1]。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__getitem__(self,index):ifindexinrange(-2,2):returnself.yifindexin(1,-1)elseself.xraiseIndexError("Indexoutofrange")def__setitem__(self,index,value):ifindexin(0,-2):self.x=valueelifindexin(1,-1):self.y=valueelse:raiseIndexError("Indexoutofrange.")'运行运行测试:>>>a=Point(1,2)>>>a[0],a[1](1,2)>>>a[-1],a[-2](2,1)>>>a[0]=5>>>aPoint(5,2)>>>a[1]=3>>>aPoint(5,3)>>>[iforiina][5,3]>>>x,y=a>>>x5>>>y3>>>b=iter(a)>>>next(b)5>>>next(b)3>>>next(b)Traceback(mostrecentcalllast): File" ",line1,in  next(b)StopIteration__iter____next__共同定义一个对象的迭代行为,迭代器必须实现__iter__()方法,该方法返回迭代器自身,或者返回一个新的迭代器对象。__next__()方法返回迭代器的下一个元素。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,yself.index=0def__repr__(self):returnf'Point({self.x},{self.y})'def__iter__(self):self.index=0returnselfdef__next__(self):ifself.index>>a=Point(5,3)>>>x,y=a>>>x,y(5,3)>>>next(a)Traceback(mostrecentcalllast): File" ",line1,in  next(a) File" ",line16,in__next__  raiseStopIterationStopIteration>>>a=Point(5,3)>>>next(a)5>>>next(a)3>>>aPoint(5,3)>>>a.x5>>>next(a)Traceback(mostrecentcalllast): File" ",line1,in  next(a) File" ",line16,in__next__  raiseStopIterationStopIteration>>>a[0]Traceback(mostrecentcalllast): File" ",line1,in  a[0]TypeError:'Point'objectisnotsubscriptable对于点类说,可迭代魔法方法完全可弃用;因为使用__getitem__方法和iter()函数已有此功能。__len__求长度的方法,原义就是计算可迭代对象元素的个数;点类的长度就是2。def__len__(self):return2'运行运行__neg__求相反数的方法,也就是单目的“ -”符号;重载为横纵坐标都取相反数。def__neg__(self):returnPoint(-self.x,-self.y)'运行运行__pos__这是单目的“ +”符号,一般无需重新定义;但是我们还是把它重载成穿过点的横纵两条直线上所有的整数点坐标,还是有点象形的,如一个十字架。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__pos__(self):n=0whileTrue:yieldPoint(n,self.y),Point(self.x,n)n+=1'运行运行测试:>>>a=Point(2,4)>>>b=+a>>>next(b)(Point(0,4),Point(2,0))>>>next(b)(Point(1,4),Point(2,1))>>>next(b)(Point(2,4),Point(2,2))>>>next(b)(Point(3,4),Point(2,3))>>>next(b)(Point(4,4),Point(2,4))>>>next(b)(Point(5,4),Point(2,5))>>>b=+a>>>horizontal=[next(b)[0]for_inrange(5)]>>>horizontal[Point(0,4),Point(1,4),Point(2,4),Point(3,4),Point(4,4)]>>>b=+a>>>vertical=[next(b)[1]for_inrange(5)]>>>vertical[Point(2,0),Point(2,1),Point(2,2),Point(2,3),Point(2,4)] 这种设计返回的点太多,使用并不方便。有必要改成只返回上下左右相邻的四个点:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__pos__(self):returnPoint(self.x,self.y+1),Point(self.x,self.y-1),Point(self.x-1,self.y),Point(self.x+1,self.y)'运行运行__abs__求绝对值的方法,重载时定义为把横纵坐标都取绝对。def__abs__(self):returnPoint(*map(abs,(self.x,self.y)))'运行运行以上三种方法不改变类自身,注意以下写法会使类改变自身:def__neg__(self):self.x=-self.xreturnselfdef__pos__(self):self.y=-self.yreturnselfdef__abs__(self):self.x,self.y=map(abs,(self.x,self.y))returnself__bool__布尔值方法,重载时定义为点处在坐标系第一象限及其边界上,就返回True;否则返回False。def__bool__(self):returnself.x>=0andself.y>=0'运行运行__call__ 这个魔术方法比较特殊,它允许一个类像函数一样被调用;我们借此定义一个点的移动。def__call__(self,dx=0,dy=0):returnPoint(self.x+dx,self.y+dy)'运行运行测试:>>>a=Point(-5,5)>>>b=a(3,2)>>>bRc(-2,7)>>>b=b(3,2)>>>bRc(1,9)>>>aRc(-5,5)扩展一下__call__方法,让它除了能移动点还能计算点到点的实际距离:def__call__(self,dx=0,dy=0,distance=False):ifdistance:return((self.x-dx)**2+(self.y-dy)**2)**0.5returnPoint(self.x+dx,self.y+dy)'运行运行测试: >>>a=Point(3,4)>>>a(0,0,True)5.0>>>len(a)5>>>a(*a(1,1),True)1.4142135623730951>>>aRc(3,4)>>>a(2,3,True)1.4142135623730951注:一旦定义了__call__这个方法,__pos__方法就能改进得更简洁。classPoint:def__init__(self,r=0,c=0):self.x,self.y=r,cdef__repr__(self):returnf'Point({self.x},{self.y})'def__pos__(self):returnself(0,1),self(0,-1),self(-1),self(1)def__call__(self,dx=0,dy=0):returnPoint(self.x+dx,self.y+dy)'运行运行测试: >>>a=Point()>>>+a(Point(0,1),Point(0,-1),Point(-1,0),Point(1,0))>>>+Point()  #直接返回上下左右四个方向(Point(0,1),Point(0,-1),Point(-1,0),Point(1,0))>>>b=Point(7,8)>>>+b(Point(7,9),Point(7,7),Point(6,8),Point(8,8))最后,综合以上所有有用的魔术方法,代码如下: classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__str__(self):returnf'({self.x},{self.y})'def__getitem__(self,index):ifindexinrange(-2,2):returnself.yifindexin(1,-1)elseself.xraiseIndexError("Indexoutofrange")def__setitem__(self,index,value):ifindexin(0,-2):self.x=valueelifindexin(1,-1):self.y=valueelse:raiseIndexError("Indexoutofrange.")def__len__(self):return2def__abs__(self):returnPoint(*map(abs,(self.x,self.y)))def__bool__(self):returnself.x>=0andself.y>=0def__neg__(self):returnPoint(-self.x,-self.y)def__pos__(self):returnself(0,1),self(0,-1),self(-1),self(1)def__call__(self,dx=0,dy=0):returnPoint(self.x+dx,self.y+dy)'运行运行重载运算符python中,常用的运算符都有对应的魔法方法可以重新定义新的运算操作。比较运算符相等==两个点相等,就是它俩的横纵坐标分别相等。def__eq__(self,other):returnself.x==other.xandself.y==other.y'运行运行为使得类更强健,可以对参数other作一类型判断:  def__eq__(self,other):    assert(isinstance(other,Point))    returnself.x==other.xandself.y==other.y 或者:  def__eq__(self,other):    ifisinstance(other,Point):      returnself.x==other.xandself.y==other.y    else:      raiseTypeError("OperandmustbeaninstanceofPoint")不等!=def__ne__(self,other):returnself.x!=other.xorself.y!=other.y'运行运行也可以这样表示:  def__ne__(self,other):    returnnotself.__eq__(other)因为notself.x==other.xandself.y==other.y即notself.x==other.xornot self.y==other.y。经测试,有了__eq__,__ne__可有可无,直接可以用!=运算。>>>classPoint:...  def__init__(self,x=0,y=0):...    self.x,self.y=x,y...  def__eq__(self,other):...    returnself.x==other.xandself.y==other.y... ...   >>>a=Point(2,5)>>>b=Point(2,5)>>>c=Point(1,3)>>>a==aTrue>>>a==bTrue>>>a==cFalse>>>a!=bFalse>>>b!=cTrue >>>classPoint:...  def__init__(self,x=0,y=0):...    self.x,self.y=x,y...  def__eq__(self,other):...    returnself.x==other.xandself.y==other.y...  def__ne__(self,other):...    returnself.x!=other.xorself.y!=other.y... ...   >>>a=Point(2,5)>>>b=Point(2,3)>>>a!=bTrue大于和小于>、判断右边的纵坐标是否相等;但纵横坐标不能同时相等。实际用处就是判断两点是否在同一水平线或同一垂直线上。def__lt__(self,other):returnself.x==other.xandself.y!=other.ydef__gt__(self,other):returnself.x!=other.xandself.y==other.y大于等于和小于等于>=、=纵坐标相等时计算横坐标的差。def__le__(self,other):returnself.x==other.xandself.y-other.ydef__ge__(self,other):returnself.y==other.yandself.x-other.x测试:>>>a=Point(2,5)>>>b=Point(2,3)>>>a>>b>>a>=bFalse>>>b>=aFalse>>>a=Point(5,1)>>>b=Point(2,1)>>>a>=b3>>>b>=a-3>>>aaTruebbTruec=Point(2,2)c>=c0c>cFalse0isFalseFalse0==FalseTrue基于以上结果,只要注意对相同两点判断时,使用==False可能误判,因为0isFalse,但用is来判断就能区别开来,isFalse和is0效果是不相同。所以我们把>=和和和>=管右边的纵坐标。修改后的所有比较运算符的重载代码如下:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__str__(self):returnf'({self.x},{self.y})'def__eq__(self,other):returnself.x==other.xandself.y==other.ydef__ne__(self,other):returnself.x!=other.xorself.y!=other.ydef__gt__(self,other):returnself.x==other.xandself.y-other.ydef__lt__(self,other):returnself.y==other.yandself.x-other.xdef__ge__(self,other):returnself.x==other.xandabs(self.y-other.y)==1def__le__(self,other):returnself.y==other.yandabs(self.x-other.x)==1'运行运行位运算符位运算符是一类专门用于处理整数类型数据的二进制位操作的运算符。位与&原义是对两个数的二进制表示进行按位与操作,只有当两个位都为1时,结果位才为1,否则为0。位或|原义是对两个数的二进制表示进行按位或操作,只要有一个位为1,结果位就为1。==和 !=分别表示横纵坐标 “x,y都相等”和“至少有一个不等”,互为反运算;那就把 与&和或 | 重载成“x,y都不相等”和“至少有一个不等”,正好也互为反运算。  def__and__(self,other):    returnself.x!=other.xandself.y!=other.y  def__or__(self,other):    returnself.x==other.xorself.y==other.y可以理解为:==是严格的相等,“与”是严格的不等;“或”是不严格的相等,!=是不严格的不等。位异或^原义是对两个数的二进制表示进行按位异或操作,当两个位不相同时,结果位为1;相同时为0。那就把 异或^重载成横纵坐标 “x,y有且只有一个值相等”,非常接近异或的逻辑意义。  def__xor__(self,other):    returnself.x==other.xandself.y!=other.yorself.x!=other.xandself.y==other.y位取反~ 原义是将整数的二进制每一位进行取反操作,即将0变为1,将1变为0。对一个十进制整数n来说,~n==-n-1;有个妙用列表的索引从0开始,索引下标0,1,2,3表示列表的前4个,而~0,~1,~2,~3正好索引列表的倒数4个元素,因为它们分别等于-1,-2,-3,-4。取反重载时,我们把它定义成交换坐标点的纵横坐标。def__invert__(self):returnPoint(self.y,self.x)'运行运行测试:>>>a=Point(1,5)>>>aPoint(1,5)>>>~aPoint(5,1)>>>aPoint(1,5)>>>a=~a>>>aPoint(5,1)左位移<< 位移运算符的原义是将整数的二进制位全部左移或右移指定的位数。左移时,低位溢出的位被丢弃,高位空出的位用0填充;左移运算相当于对数值进行乘以2的运算 。 右位移 >>右移运算对于有符号整数,右移时会保留符号位(即最高位),并在左侧填充与符号位相同的位。对于无符号整数,右移时左侧填充0;每次右移相当于将数值整除2的运算。位移运算符重载时采用和比较运算符重载时相同的箭头指向性,即左位移管横坐标的移动,右位移管纵坐标的位移,此时other为整数,正整数指坐标点向右移动或向上移动;负整数刚好相反。def__lshift__(self,other):returnPoint(self.x+other,self.y)def__rshift__(self,other):returnPoint(self.x,self.y+other)测试:>>>a=Point(5,1)aPoint(5,1)a>>4Point(5,5)a<< -4 Point(1, 1) a Point(5, 1) a >>=4aPoint(5,5)a<<=>>>a=Point(1,5)>>>b=Point(1,1)>>>a>b4>>>if(n:=a>b):...  a>>=-n... ...   >>>a==bTrue位移运算重载后,同时位移并赋值功能也生效,即>>=和<<=>>>a=Point(-5,5)>>>a<< 5 Rc(0, 5) >>>aRc(-5,5)>>>a(5)Rc(0,5)>>>aRc(-5,5)>>>a(0,-5)Rc(-5,0)>>>a>>-5Rc(-5,0)>>>aRc(-5,5) 综合所有位运算符,代码如下:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__and__(self,other):returnself.x!=other.xandself.y!=other.ydef__or__(self,other):returnself.x==other.xorself.y==other.ydef__xor__(self,other):returnself.x==other.xandself.y!=other.yorself.x!=other.xandself.y==other.ydef__invert__(self):returnPoint(self.y,self.x)def__lshift__(self,other):returnPoint(self.x+other,self.y)def__rshift__(self,other):returnPoint(self.x,self.y+other)'运行运行算术运算符算术运算很简单,除了加减乘除+,-,*,/,还有幂运算**、取模%、整除//等。加+加法很明显重载成坐标值的相加最接近加法的本义:  def__add__(self,other):    returnPoint(self.x+other.x,self.y+other.y)我们可以让other的定义域进一步扩大不仅限于是个点类,只要符合指定条件都可以“相加”,即移动为另一个点;如果“点”和不符合条件的对象相加,则返回None。指定条件为hasattr(other,'__getitem__')andlen(other)==2,重载定义如下:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__getitem__(self,index):ifindexinrange(-2,2):returnself.yifindexin(1,-1)elseself.xraiseIndexError("Indexoutofrange")def__len__(self):return2def__call__(self,dx=0,dy=0):returnPoint(self.x+dx,self.y+dy)def__add__(self,other):ifhasattr(other,'__getitem__')andlen(other)==2:returnself.__call__(*map(int,other))'运行运行测试:>>>a=Point(3,4)>>>b=Point(2,-2)>>>a+bPoint(5,2)>>>a+(1,1)Point(4,5)>>>a+'11'Point(4,5)>>>a+'1'   #None与调用__call__方法的比较:>>>p=Point(5,5)>>>d=Point(2,3)>>>p+dPoint(7,8)>>>p(d.x,d.y)Point(7,8)>>>p(2,3)Point(7,8)>>>pPoint(5,5)减-因为减一个数就是加它的相反数,所以没必要把减法运算重载成和加法一样的模式;我们可以把减法重载成两点之间的距离,重载定义为:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__getitem__(self,index):ifindexinrange(-2,2):returnself.yifindexin(1,-1)elseself.xraiseIndexError("Indexoutofrange")def__len__(self):return2def__sub__(self,other):ifhasattr(other,'__getitem__')andlen(other)==2:dx,dy=tuple(map(float,other))return((self.x-dx)**2+(self.y-dy)**2)**0.5'运行运行测试: >>>a=Point(3,4)>>>a-Point()5.0>>>a-(4,5)1.4142135623730951>>>a-'44'1.0>>>a-3  #None乘*乘法就重载为判断两点是否整数相邻,即:self>=otherorself=otherorself>>0=Point(3,5)>>>lst=(0,1),(0,-1),(-1,0),(1,0),(1,1)>>>5=[P0(x,y)forx,yinlst]>>>5[Point(3,6),Point(3,4),Point(2,5),Point(4,5),Point(4,6)]>>>[P0*pforpinP5][True,True,True,True,False]除/除法就重载为判断在同一水平线或垂直线上的两点,是正序还是反序;正序是指前左后右或前下后上,返回1;反序则相反,前右后左或前上后下,返回-1;不符条件的,则返回False。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__gt__(self,other):returnself.x==other.xandself.y-other.ydef__lt__(self,other):returnself.y==other.yandself.x-other.xdef__xor__(self,other):returnself.x==other.xandself.y!=other.yorself.x!=other.xandself.y==other.ydef__truediv__(self,other):return(self^other)and(1if(selfother)>>a=Point(1,2)>>>b=Point(5,2)>>>c=Point(5,5)>>>a/b,b/a,b/c,c/b(1,-1,1,-1)>>>a/c,c/a, a/a, c /c(False,False,False,False)幂**幂运算就重载为返回在同一水平线或垂直线上的两点之间的点;不符合条件的,则返回None。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__gt__(self,other):returnself.x==other.xandself.y-other.ydef__lt__(self,other):returnself.y==other.yandself.x-other.xdef__xor__(self,other):returnself.x==other.xandself.y!=other.yorself.x!=other.xandself.y==other.ydef__truediv__(self,other):return(self^other)and(1if(selfother)other:return[Point(self.x,_)for_inrange(self.y+(self/other),other.y,self/other)]'运行运行测试:>>>a=Point(1,2)>>>b=Point(5,2)>>>c=Point(5,5)>>>a**b[Point(2,2),Point(3,2),Point(4,2)]>>>b**a[Point(4,2),Point(3,2),Point(2,2)]>>>b**c[Point(5,3),Point(5,4)]>>>c**b[Point(5,4),Point(5,3)]>>>a**c  #None>>>a**a  #None>>>a**a==[]False>>>a**a==NoneTrue>>>a**c==NoneTrue取模 %取模运算就重载为返回所给两点作对角线的水平矩形的另外两个端点;如果所得矩形面积为0,则返回值就是原来所给的两点。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__mod__(self,other):return[Point(self.x,dy),Point(dx,self.y)]'运行运行测试:>>>a=Point(1,2)>>>b=Point(5,5)>>>a%b[Point(1,5),Point(5,2)]>>>(a%b)[0]Point(1,5)>>>(a%b)[1]Point(5,2)>>>c,d=a%b>>>c%d[Point(1,2),Point(5,5)]>>>c%a[Point(1,2),Point(1,5)]>>>c%b[Point(1,5),Point(5,5)]示意图: 整除// 整除运算就重载为返回所给两点作对角线的矩形上的两组邻边上的所有点;返回点的列表也分组,如上图,一组是路径a->c->b上的点,另一级是路径a->d->b上的点。classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__gt__(self,other):returnself.x==other.xandself.y-other.ydef__lt__(self,other):returnself.y==other.yandself.x-other.xdef__and__(self,other):returnself.x!=other.xandself.y!=other.ydef__xor__(self,other):returnself.x==other.xandself.y!=other.yorself.x!=other.xandself.y==other.ydef__mod__(self,other):return[Point(self.x,other.y),Point(other.x,self.y)]def__truediv__(self,other):return(self^other)and(1if(selfother)other:return[Point(self.x,_)for_inrange(self.y+(self/other),other.y,self/other)]def__floordiv__(self,other):ifself&other:mod1,mod2=self%otherreturnself**mod1+[mod1]+mod1**other,self**mod2+[mod2]+mod2**other'运行运行测试: >>>a=Point(1,2)>>>b=Point(5,5)>>>a//b([Point(1,3),Point(1,4),Point(1,5),Point(2,5),Point(3,5),Point(4,5)],[Point(2,2),Point(3,2),Point(4,2),Point(5,2),Point(5,3),Point(5,4)])>>>b//a([Point(5,4),Point(5,3),Point(5,2),Point(4,2),Point(3,2),Point(2,2)],[Point(4,5),Point(3,5),Point(2,5),Point(1,5),Point(1,4),Point(1,3)])右加+大部分运算符都有对应的右侧运算操作,由字母r开头,如右加__radd__、右减__rsub__、右乘__rmul__......等等;只有当自定义对象位于运算符号的右侧时,右运算方法才会被调用。与普通的加法相比,__radd__(self,other) 方法只有当other在加号+的左边,对象在加号+的右边才会被调用。我们把右加方法重载成单目运算+差不多的规则,只是多了距离n,不一定是相邻的四点:def__radd__(self,n):returnself(0,n),self(0,-n),self(-n),self(n)'运行运行测试: >>>a=Point(1,2);b=Point();n=2>>>a+bPoint(1,2)>>>b+aPoint(1,2)>>>a+n Traceback(mostrecentcalllast): File" ",line1,in  a+nAttributeError:'int'objecthasnoattribute'x'>>>n+a(Point(1,4),Point(1,0),Point(-1,2),Point(3,2))>>>n+b(Point(0,2),Point(0,-2),Point(-2,0),Point(2,0))>>>1+b #当n==1就返回上下左右相邻的四个点(Point(0,1),Point(0,-1),Point(-1,0),Point(1,0))>>>1+b==+bTrue>>>-1+b(Point(0,-1),Point(0,1),Point(1,0),Point(-1,0))>>>0+b(Point(0,0),Point(0,0),Point(0,0),Point(0,0)) 右幂**右整除//分别返回两点间的水平线或垂直线上所有的整数点,other是由横纵坐标x,y与n组成的三元元组。def__rpow__(self,other):assert(isinstance(other,tuple)andlen(other)==3)x,y,n=otherreturn[Point(i,n)foriinrange(min(x,self.x),max(x,self.x)+1)]def__rfloordiv__(self,other:tuple):assert(isinstance(other,tuple)andlen(other)==3)x,y,n=otherreturn[Point(n,i)foriinrange(min(y,self.y),max(y,self.y)+1)]测试:>>>a=Point(1,2)>>>b=Point(5,5)>>>(a.x,a.y,4)**b[Point(1,4),Point(2,4),Point(3,4),Point(4,4),Point(5,4)]>>>(*b,4)**a[Point(1,4),Point(2,4),Point(3,4),Point(4,4),Point(5,4)]>>>(a.x,a.y,3)//b[Point(3,2),Point(3,3),Point(3,4),Point(3,5)]>>>(*a,3)//b[Point(3,2),Point(3,3),Point(3,4),Point(3,5)]>>>(*b,3)//a[Point(3,2),Point(3,3),Point(3,4),Point(3,5)]示意图:总结本文通过魔法方法的巧妙使用,为Point类定义了丰富多彩的“花样”功能,使其不仅能够表示一个二维空间中的点,还能够执行各种运算和操作。例如,我们可以重载加法运算符来计算两个点之间的移动,重载比较运算符来判断两个点是否在同一直线上,或者重载位运算符来交换点的横纵坐标等。本篇共涉及了四大类30多种魔法方法:算术运算符:包括加__add__、右加__radd__、减__sub__、乘__mul__、除__truediv__、取模__mod__、整除__floordiv__、__rfloordiv__和幂__pow__、__rpow__。比较运算符:包括等于__eq__、不等于__ne__、小于__lt__、大于__gt__、小于等于__le__和大于等于__ge__。位运算符:包括位与__and__、位或__or__、位异或__xor__、位取反__invert__、左位移<< 和右位移 >>。其他魔术方法:如显示__repr__、转字符串__str__、求长度__len__、单目正负操作符 __pos__,__neg__、求绝对值__abs__、布尔值__bool__、下标获取和设置 __getitem__, __setitem__以及迭代功能 __iter__, __next__。在实际编程中,我们应该根据实际需求来决定是否需要重载这些运算符;但过度使用运算符重载可能会导致代码难以理解和维护,而恰当的使用则可以提高代码的可读性和效率。总的来说,运算符重载是一种强大的工具,它可以让自定义类型更加自然地融入到Python的生态系统中。全部完整代码:classPoint:def__init__(self,x=0,y=0):self.x,self.y=x,ydef__repr__(self):returnf'Point({self.x},{self.y})'def__str__(self):returnf'({self.x},{self.y})'def__getitem__(self,index):ifindexinrange(-2,2):returnself.yifindexin(1,-1)elseself.xraiseIndexError("Indexoutofrange")def__setitem__(self,index,value):ifindexin(0,-2):self.x=valueelifindexin(1,-1):self.y=valueelse:raiseIndexError("Indexoutofrange.")@propertydefvalue(self):returnself.x,self.ydef__len__(self):return2def__abs__(self):returnPoint(*map(abs,(self.x,self.y)))def__bool__(self):returnself.x>=0andself.y>=0def__neg__(self):returnPoint(-self.x,-self.y)def__pos__(self):returnself(0,1),self(0,-1),self(-1),self(1)def__call__(self,dx=0,dy=0):returnPoint(self.x+dx,self.y+dy)def__eq__(self,other):returnself.x==other.xandself.y==other.ydef__ne__(self,other):returnself.x!=other.xorself.y!=other.ydef__gt__(self,other):returnself.x==other.xandself.y-other.ydef__lt__(self,other):returnself.y==other.yandself.x-other.xdef__ge__(self,other):returnself.x==other.xandabs(self.y-other.y)==1def__le__(self,other):returnself.y==other.yandabs(self.x-other.x)==1def__and__(self,other):returnself.x!=other.xandself.y!=other.ydef__radd__(self,n):returnself(0,n),self(0,-n),self(-n),self(n)def__or__(self,other):returnself.x==other.xorself.y==other.ydef__xor__(self,other):returnself.x==other.xandself.y!=other.yorself.x!=other.xandself.y==other.ydef__invert__(self):returnPoint(self.y,self.x)def__lshift__(self,other):returnPoint(self.x+other,self.y)def__rshift__(self,other):returnPoint(self.x,self.y+other)def__add__(self,other):returnPoint(self.x+other.x,self.y+other.y)def__sub__(self,other):return((self.x-other.x)**2+(self.y-other.y)**2)**0.5def__mul__(self,other):returnself>=otherorselfother)other:return[Point(self.x,_)for_inrange(self.y+(self/other),other.y,self/other)]def__mod__(self,other):return[Point(self.x,other.y),Point(other.x,self.y)]def__floordiv__(self,other):ifself&other:mod1,mod2=self%otherreturnself**mod1+[mod1]+mod1**other,self**mod2+[mod2]+mod2**otherdef__rpow__(self,other):assert(isinstance(other,(tuple,list))andlen(other)==3)x,y,n=otherreturn[Point(i,n)foriinrange(min(x,self.x),max(x,self.x)+1)]def__rfloordiv__(self,other:tuple):assert(isinstance(other,(tuple,list))andlen(other)==3)x,y,n=otherreturn[Point(n,i)foriinrange(min(y,self.y),max(y,self.y)+1)]'运行运行注:把这些代码保存为文件pointlib.py,可以当作一个自定义库来使用。这个自定义库的实例操作请见链接:Python一步一步教你用pyglet制作“彩色方块连连看”游戏(续)-CSDN博客完。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-10 22:54 , Processed in 0.811415 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表