前言:
而今兄弟们对“运算符重载的写法”大约比较关切,看官们都需要剖析一些“运算符重载的写法”的相关文章。那么小编也在网络上搜集了一些关于“运算符重载的写法””的相关文章,希望朋友们能喜欢,看官们一起来学习一下吧!“运算符重载”只是意味着在类方法中拦截内置的操作——当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果。以下是对重载的关键概念的复习:
·运算符重载让类拦截常规的Python运算。
·类可重载所有Python表达式运算符。
·类也可重载打印、函数调用、属性点号运算等内置运算。
·重载使类实例的行为像内置类型。
·重载是通过提供特殊名称的类方法来实现的。
换句话说,当类中提供了某个特殊名称的方法,在该类的实例出现在它们相关的表达式时,Python自动调用它们。正如我们已经学过的,运算符重载方法并非必需的,并且通常也不是默认的;如果你没有编写或继承一个运算符重载方法,只是意味着你的类不会支持相应的操作。然而,当使用的时候,这些方法允许类模拟内置对象的接口,因此表现得更一致。
常见的运算符重载方法
注意:所有重载方法的名称前后都有两个下划线字符,以便把同类中定义的变量名区别开来。特殊方法名称和表达式或运算的映射关系,是由Python语言预先定义好的(在标准语言手册中有说明)。例如,名称__add__按照Python语言的定义,无论__add__方法的代码实际在做些什么,总是对应到了表达式+。
在Python中,如果我们想实现创建类似于序列和映射的类(可以迭代以及通过[下标]返回元素),可以通过重写__getitem__、__setitem__、__delitem__、__len__方法去模拟。
__getitem__(self,key):返回键对应的值。
__setitem__(self,key,value):设置给定键的值
__delitem__(self,key):删除给定键对应的元素。
__len__():返回元素的数量
只要实现了__getitem__和 __len__方法,就会被认为是序列。而序列则可以迭代,比如用于for循环。
下面来看一下__getitem__的基本用法:
这里出现了slice对象,slice() 函数实现切片对象,主要用在切片操作函数里的参数传递。
参数说明:
start -- 起始位置stop -- 结束位置step -- 间距
例:
a_slice=slice(2,6,2) #从2 开始,到6 结束(不包含在内),每隔2取数
data=[1,2,3,4,5,6,7,8,9]
print(data[a_slice])
输出:[3,5]
__setitem__的使用方法类似于__getitem__,起作用是通过切片的方法设置索引值:
__getitem__的索引迭代:
如果定义了此方法,for循环每次都会调用这个方法,从而产生更高的偏移值,甚至可以自定义偏移值递增方法:
自定义迭代器
为了更好理解,先介绍一组基本概念:
Iterable: 有迭代能力的对象,一个类,实现了__iter__,那么就认为它有迭代能力,通常此函数必须返回一个实现了__next__的对象,如果自己实现了,你可以返回self,当然这个返回值不是必须的;Iterator: 迭代器(当然也是Iterable),同时实现了__iter__和__next__的对象,缺少任何一个都不算是Iterator。可以使用collection.abs里面的Iterator和Iterable配合isinstance函数来判断一个对象是否是可迭代的,是否是迭代器对象只要实现了__iter__的对象就是可迭代对象(Iterable),正常情况下,应该返回一个实现了__next__的对象(虽然这个要求不强制),如果自己实现了__next__,当然也可以返回自己同时实现了__iter__和__next__的是迭代器(Iterator),当然也是一个可迭代对象了,其中__next__应该在迭代完成后,抛出一个StopIteration异常for语句会自动处理这个StopIteration异常以便结束for循环
例:生成一组平方数
有多个迭代器的对象:要达到多个迭代器的效果,__iter__只需要定义新的状态对象,而不是返回self。
属性引用:__getattr__与__setattr__
正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。要避免这个错误,除了可以加上一个属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。
class student: def __init__(self,name): self.name=namest=student('shun')st.name'shun'st.ageAttributeError: 'student' object has no attribute 'age' #报错
这时我们尝试重写属性:
class student: def __init__(self,name): self.name=name def __getattr__(self,attr): if attr=='age': return 20 else : raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)st=student('shun')st.name'shun'st.age20
__setattr__(self, item, value):
当试图对象的item特性赋值的时候将会被调用。如果定义了这个方法,不要使用self.attr=value,这样会引起循环调用,正确的做法是使用属性字典做索引。
def __setattr__(self,key,value): print('into __setattr__') self.__dict__[key]=value st=student('shun') #构造函数也会调用自己定义的__setattr__st.name #因此你也可以通过设置__setattr__来让构造函数完成一些额外的工作into __setattr__'shun'st.age=21st.ageinto __setattr__21
应用:模拟实例属性的私有性
class PrivateExc(Exception): #定义异常类 passclass Privacy(PrivateExc): def __getattr__(self,attr): if attr in self.Privates: raise PrivateExc(attr,self) else: return self.__dict__[attr] def __setattr__(self,attr,value): if attr in self.Privates: raise PrivateExc(attr,self) else: self.__dict__[attr]=valueclass Test1(Privacy): Privates=['age'] def __init__(self,name): self.__dict__['name']=name a=Test1('shun')a.name'shun'a.age=21PrivateExc: ('age', Test1('shun'))__add__和__radd__ 和 __iadd__
__radd__是自定义的类操作符,执行“右加”。
当python解释器执行到a+b这样的语句时,首先在查找a中有没有__add__操作符,如果a中没有定义,那么就在b中查找并执行__radd__。
至于__iadd__(),是运算符类operator的成员函数,就是累加操作符的另一种调用形式。a = operator.__iadd__(a, b)就等价于a += b
def __add__(self, other)#该类对象+别的对象时调用 return #加的结果def __radd__(self, other)#别的对象+该类对象时调用 return #加的结果
class Point: def __init__(self,x,y): self.x=x self.y=y def __add__(self,other): if isinstance(other,tuple): return Point(self.x+other[0],self.y+other[1]) else: return Point(self.x+other.x,self.y+other.y) def __radd__(self,other): return Point(self.x+other[0],self.y+other[1]) def __str__(self): return '(%.2f,%.2f)'%(self.x,self.y) def __repr__(self): return '(%.2f,%.2f)'%(self.x,self.y) a=Point(0,0)b=Point(1.2,1.2)a+=(1,2)(1.00,2.00)(1,2)+a(2.00,4.00)#如果没有__radd__会报错
其他__mul__,__rmul__的用法类似。
__call__
直白来说可以让对象名当作函数名传递参数,继续上面的point类:
def __call__(self): return math.sqrt(self.x**2+self.y**2) a()2.23606797749979比较重载
def __eq__(self,other): return (self.x==other.x and self.y==other.y) def __lt__(self,other): return self()<other() def __gt__(self,other): return self()>other()a=Point(0,0)b=Point(1.2,1.2)a==bFalsea<bTrue
实例:自定义数组类
from operator import mul,addclass MyArray: '''This an array used for storing numbers''' def __isNumber(n): return isinstance(n,(int,float)) def __init__(self,List=[]): '''filte other types''' self.__value=list(filter(MyArray.__isNumber,List)) def __del__(self): #析构函数,释放内部封装的列表 del self.__value def __str__(self): return str(self.__value) def __len__(self): return len(self.__value) def append(self,List):#向数组中添加元素 if __isNumber(List): self.__value.append(List) elif isinstance(List,list): self.__value.append(list(filter(MyArray.__isNumber,List))) else: pass #不是数字类型,不做任何处理 def __add__(self,n): b=MyArray() if MyArray.__isNumber(n): b.__value=[i+n for i in self.__value] elif isinstance(n,MyArray): m1,m2=self.__value.copy(),n.__value.copy() while any([m1,m2]): #自动根据长度进行扩展 b.__value.append((m1.pop(0) if m1 else 0) +(m2.pop(0) if m2 else 0) ) elif isinstance(n,list): #对列表进行加 tmp=MyArray(n) return self+tmp return b def dot(a,b):#计算内积 return sum(map(mul,a.__value,b.__value)) def __getitem__(self,index): return self.__value[index] def __setitem__(self,index,value): self.__value[index]=value
下一篇:类的设计方法简介
标签: #运算符重载的写法