最近在学习python高级特性,尤其是关于python类与python设计的运算系统规则。这篇文章总结了一些关于python中运算符重载的设计技巧,在自定义类或者开发自定义库中尤其常见。
第一部分:概览
什么是运算符重载 让自定义的类生成的对象(实例)能够使用运算符进行操作 作用: 让自定义的实例像内建对象一样进行运算符操作 让程序简洁易读 对自定义对象将运算符赋予新的规则 算术运算符的重载: 方法名 运算符和表达式 说明 __add__(self,rhs) self + rhs 加法 __sub__(self,rhs) self - rhs 减法 __mul__(self,rhs) self * rhs 乘法 __truediv__(self,rhs) self / rhs 除法 __floordiv__(self,rhs) self //rhs 地板除 __mod__(self,rhs) self % rhs 取模(求余) __pow__(self,rhs) self **rhs 幂运算 示例:
return
"Mynumber(%d)" %self.data
'''此方法用来制定self + other的规则'''
v = self.data + other.data
'''此方法用来制定self - other的规则'''
v = self.data - other.data
rhs(right hand side) 右手边
说明: 运算符重载的方法的参数已经有了固定的含义,不建议改变原有的运算符的含义及参数的意义
二元运算符的重载方法格式: def __xx__(self,other): 语句块
练习: 实现两个自定义列表的相加 class Mylist: def __init__(self,iterable=()): self.data = list(iterable)
L1 = MyList([1,2,3]) L2 = MyList([4,5,6]) L3 = L1+L2 print(L3) #MyList([1,2,3,4,5,6]) L4 = L2 + L3 print(L4) #MyList([4,5,6,1,2,3])
#试想能否实现以下操作 L5 = L1 * 3 print(L5) #MyList([1,2,3,1,2,3,1,2,3])
def __init__ (self, iterable=() ) :
self.data = list(iterable)
return
'Mylist(%s)' % self.data
return Mylist(self.data + lst.data)
return Mylist(self.data * rhs)
反向运算符的重载 当运算符的左侧为内建类型时,右侧为自定义类型进行算术匀算符运算时会出现TypeError错误,因为无法修改内建类型的代码 实现运算符重载,此时需要使用反向运算符的重载 反向算术运算符的重载: 方法名 运算符和表达式 说明 __radd__(self,lhs) lhs + self 加法 __rsub__(self,lhs) lhs - self 减法 __rmul__(self,lhs) lhs * self 乘法 __rtruediv__(self,lhs) lhs / self 除法 __rfloordiv__(self,lhs) lhs // self 地板除 __rmod__(self,lhs) lhs % self 取模(求余) __rpow__(self,lhs) lhs ** self 幂运算 示例:
def __init__ (self, iterable=() ) :
self.data = list(iterable)
return
'Mylist(%s)' % self.data
return Mylist(self.data + lst.data)
return Mylist(self.data * rhs)
return Mylist(self.data * lhs)
复合赋值算术运算符的重载 以复合赋值算术运算符 x += y为例,此运算符会优先调用x.__iadd__(y)方法,如果没有__iadd__方法时,则会将复合赋值算术运 算拆解为:x = x + y 然后调用x = x.__add__(y)方法,如果再不存在__add__方法则会触发TypeError类型的错误异常
复合赋值算术运算符的重载: 方法名 运算符和表达式 说明 __iadd__(self,rhs) self += rhs 加法 __isub__(self,rhs) self -= rhs 减法 __imul__(self,rhs) self *= rhs 乘法 __itruediv__(self,rhs) self /= rhs 除法 __ifloordiv__(self,rhs) self //=rhs 地板除 __imod__(self,rhs) self %= rhs 取模(求余) __ipow__(self,rhs) self **=rhs 幂运算
比较算术运算符的重载 比较算术运算符的重载: 方法名 运算符和表达式 说明 __lt__(self,rhs) self < rhs 小于 __le__(self,rhs) self <= rhs 小于等于 __gt__(self,rhs) self > rhs 大于 __ge__(self,rhs) self >= rhs 大于等于 __eq__(self,rhs) self == rhs 等于 __ne__(self,rhs) self != rhs 不等于
位运算符重载
方法名 运算符和表达式 说明 __and__(self,rhs) self & rhs 位与 __or__(self,rhs) self | rhs 位或 __xor__(self,rhs) self ^ rhs 位异或 __lshift__(self,rhs) self <<rhs 左移 __rshift__(self,rhs) self >>rhs 右移
反向位运算符重载
方法名 运算符和表达式 说明 __and__(self,lhs) lhs & rhs 位与 __or__(self,lhs) lhs | rhs 位或 __xor__(self,lhs) lhs ^ rhs 位异或 __lshift__(self,lhs) lhs <<rhs 左移 __rshift__(self,lhs) lhs >>rhs 右移
复合赋值位相关运算符重载 方法名 运算符和表达式 说明 __iand__(self,rhs) self & rhs 位与 __ior__(self,rhs) self | rhs 位或 __ixor__(self,rhs) self ^ rhs 位异或 __ilshift__(self,rhs) self <<rhs 左移 __irshift__(self,rhs) self >>rhs 右移
一元运算符的重载
方法名 运算符和表达式 说明 __neg__(self) - self 负号 __pos__(self) + self 正号 __invert__(self) ~ self 取反
语法: class 类名: def __xxx__(self): pass
示例见:
def __init__ (self, iterable=() ) :
self.data = list(iterable)
return
'Mylist(%s)' % self.data
g = (-x
for x
in self.data)
g = (abs(x)
for x
in self.data)
l1 = Mylist([
1 ,
-2 ,
3 ,
-4 ,
5 ,
-6 ])
in/not in 运算符重载 格式: def __contains__(self,e): 语句 注: in / not in 返回布尔值 True / False 当重载了__contains__后,in和not in运算符都可用 not in 运算符的返回值与 in相反 示例:
def __init__ (self, iterable=() ) :
self.data = list(iterable)
return
'Mylist(%s)' % self.data
def __contains__ (self, e) :
return
True
if e
in self.data
else
False
l1 = Mylist([
1 ,
2 ,
3 ,
4 ,
-5 ,
6 ])
索引和切片运算符重载方法: 方法名 运算符和表达式 说明 __getitem__(self,i) x = self(i) 索引/切片取值 __setitem__(self,i,v) self[i] = v 索引/切片赋值 __delitem__(self,i) del self[i] del语句删除索引/切片
作用: 让自定义的类型的对象能够支持索引和切片操作 示例:
def __init__ (self, iterable=() ) :
self.__data = list(iterable)
return
'Mylist(%s)' % self.__data
def __getitem__ (self, i) :
def __setitem__ (self, i, v) :
'''此方法可以让自定义的列表支持索引赋值操作'''
print(
'__setitem__被调用,i = ' , i,
'v = ' , v)
def __delitem__ (self, i) :
print(
'切片的起点是:' , i.start)
print(
'切片的终点是:' , i.stop)
print(
'切片的步长是:' , i.step)
l1 = Mylist([
1 ,
2 ,
3 ,
4 ,
-5 ,
6 ])
slice构造函数 作用: 用于创建一个slice对象,此对于用于切片操作的传值 格式: slice(start = None,stop = None ,step = None) slice对象的实例属性: start 切片的起始值,默认为None stop 切片的终止值,默认为None step 切片的步长,默认为None
特性属性@property 实现其他语言所拥有的getter和setter功能
作用: 用来模拟一个属性 通过@property装饰器,可以对模拟属性的赋值和取值加以控制 示例:
'''此方法用设置值加以限制以保证数据的准确性setter是用来数据的'''
第二部分:常用
构造函数与表达式: __init__, __sub__
常见运算符重载方法
method overload call __init__ 构造函数 对象创建: X = Class(args) __del__ 析构函数 X对象收回 __add__ 云算法+ 如果没有_iadd_, X+Y, X+=Y __or__ 运算符| 如果没有_ior_,X|Y, X|=Y _repr__, __str__ 打印,转换 print(X),repr(X),str(X) __call__ 函数调用 X(*args, **kwargs) __getattr__ 点号运算 X.undefined __setattr__ 属性赋值语句 X.any=value __delattr__ 属性删除 del X.any __getattribute__ 属性获取 X.any __getitem__ 索引运算 X[key],X[i:j] __setitem__ 索引赋值语句 X[key],X[i:j]=sequence __delitem__ 索引和分片删除 del X[key],del X[i:j] __len__ 长度 len(X),如果没有__bool__,真值测试 __bool__ 布尔测试 bool(X) __lt__, __gt__, __le__, __ge__, __eq__, __ne__ 特定的比较 X<Y,X>Y,X<=Y,X>=Y, X==Y,X!=Y 注释:(lt: less than, gt: greater than, le: less equal, ge: greater equal, eq: equal, ne: not equal ) __radd__ 右侧加法 other+X __iadd__ 实地(增强的)加法 X+=Y(or else __add__) __iter__, __next__ 迭代环境 I=iter(X), next() __contains__ 成员关系测试 item in X(任何可迭代) __index__ 整数值 hex(X), bin(X), oct(X) __enter__, __exit__ 环境管理器 with obj as var: __get__, __set__, __delete__ 描述符属性 X.attr, X.attr=value, del X.attr __new__ 创建 在__init__之前创建对象
索引和分片: __getitem__, __setitem__
如果在类中定义的话,则对于实例的索引运算,会自动调用__getitem__。当实例X出现X[i]这样的索引运算时,Python会自动调用__getitem__方法
拦截分片
索引迭代: __getitem__
迭代器对象: __iter__, __next__
尽管上一节__getitem__是有效的,但它是迭代退而求其次的方法。Python所有的迭代环境会有优先尝试__iter__的方法,再尝试__getitem__。
从技术角度上讲,迭代环境是通过iter去尝试寻找__iter__方法来实现,而这种方法返回一个迭代器对象。如果已经提供了,python会重复调用迭代器对象的next()方法,直到发生StopIteration异常。如果没有找到__iter__,python会使用__getitem__机制。
__getattr__和__setattr__捕捉属性的的引用
__getattr__拦截属性.运算符
__repr__和__str__会返回字符串表达形式
__radd__处理右侧加法
__call__拦截调用
当实例调用时,使用__call__方法
__del__是析构器
当实例创建时,就会调用__init__构造方法。当实例空间被收回时,析构函数__del__就会自动执行。