导语:本文章记录了本人在学习Python基础之面向对象篇的重点知识及我的心得,打算入门Python的朋友们能够来一块儿学习并交流。
本文重点:python
一、掌握运算符重载的定义和做用,以及Python对其的内部限制;
二、掌握一元运算符重载设计思路;
三、理解中缀运算符重载过程当中鸭子类型和白鹅类型思想的运用并掌握。
运算符重载:对已有的运算符进行从新定义,赋予其另外一种功能,以适应不一样的数据类型。
重载的做用:令用户定义的对象可以使用中缀运算符(如 + 和 | )或一元运算符(如 - 和 ~ )等运算符。
为了作好灵活性、可用性和安全性方面的平衡,Python对运算符重载施加了一些限制:安全
不能重载内置类型的运算符
;能新建运算符,只能重载现有运算符
;某些运算符不能重载,如is、and、or和not(不过位运算符&、| 和 ~能够)
。另外,Python语言参考手册将内置的abs()函数列为一元运算符,它对应的特殊方法是__abs__。函数
重载一元运算符只需实现相应的特殊方法,这些特殊方法只有self一个参数。重载应遵循运算符的一个基本规则:始终返回一个新对象
。
即,不能修改self,要建立并返回合适类型的新实例。学习
下面咱们以第10章的多维向量类为例重载一元运算符:spa
import math class Vector: #排版须要省略中间代码 def __abs__(self): return math.sqrt(sum(x*x for x in self)) def __neg__(self): return Vector(-x for x in self) def __pos__(self): return Vector(self) def __invert__(self): return Vector(-x-1 for x in self)
Python 3.4 为 Decimal 算术运算设定的默认精度是28,这里由于+x使用上下文的精度致使相等性判断返回False。设计
经过上面的实例可以看到counter实例ct通过零值和负值的赋值以后,再通过+x运算后发现ct实例中的非负数对象均消失了。事实上一元运算符 + 等同于加上一个空 Counter。当Counter相加时,Python解释器从实用性角度出发会把负值和零值的计数从结果中剔除。code
如今咱们仍以第10章的多维向量为例进行中缀运算符加号“+”的重载。
重载加法的目标分析:orm
当多维向量类是操做数时,多维向量应支持与同类向量的加法
;同时多维向量类还应支持与可迭代对象的加法
;此外当可迭代对象是操做数的时候,多维对象应具有__radd__如此来调用多维向量类中的__add__方法
。重载加法的流程图设计:
设计的重点在于采用鸭子类型思想。当多维向量类与非数值类相加时,多维向量类没法处理异类加法运算能够将加法运算交给右操做数的类处理。由于右操做数存在能够处理这种异类加法的可能。对象
重载加法的代码实现:继承
from itertools import zip_longest class Vector: #排版须要省略中间代码 def __add__(self, other): try: return Vector(a+b for a,b in zip_longest(self,other,fillvalue=0)) except TypeError: return NotImplemented def __radd__(self, other): return self+other
二、重载乘法__mul__
重载加法的目标分析:
当多维向量类是操做数时,多维向量应支持与同类向量的乘法
;同时多维向量类还应支持与可迭代对象的加法
;此外当可迭代对象是操做数的时候,多维对象应具有__rmul__如此来调用多维向量类中的__mul__方法
。注意:咱们对多维向量重载的乘法是针对数论中的实数类型进行运算,此时能够采用白鹅类型显式检查对象的抽象基类是否为numbers.Real,代码实现以下:
import numbers class Vector: #排版须要省略中间代码 def __mul__(self, other): if isinstance(other,numbers.Real): return Vector(x*other for x in self) else: return NotImplemented def __rmul__(self, other): return self*other
Tips:通常来讲只有当处理与self不一样类型的操做数时,须要建立反向方法处理。不然没有必要建立反向方法。
Python 解释器对众多比较运算符(==、 !=、 >、 <、 >=、 <=) 的处理与前文相似, 不过在两个方面有重大区别。
如今咱们仍以第10章的多维向量为例进行中缀运算符等号“=”的重载。
重载等号的返回为True的条件:
等号两端对象为同类对象
;等号两端对象中的每一个元素都必须对应相等
。注意:若Vector处理等号不为True,应该返回NotImplemented交由Python处理。若是反向调用返回 NotImplemented,Python 会使用后备机制比较对象的 ID,做最后一搏。
重载等号的代码实现以下:
class Vector: #排版须要省略中间代码 def __eq__(self, other): if isinstance(other,Vector): return len(self)==len(other) and all(x==y for x,y in zip(self,other)) else: return NotImplemented
def __ne__(self, other): eq_result = self == other if eq_result is NotImplemented: return NotImplemented else: return not eq_result
如今咱们仍以第10章的多维向量为例进行中缀运算符加等于“+=”的重载。
重载加等于设计要求:
加等于右侧的对象与左侧的Vector类是同类对象或可迭代对象
;不然抛出TypeError,显示没法进行加等于计算
。下面以BingoCage的子类AddableBingoCage为例实现__iadd__,你们没必要在乎这个子类,重点在于理解__iadd__实现的思路:
import itertools from tombola import Tombola from bingo import BingoCage class AddableBingoCage(BingoCage): def __add__(self, other): if isinstance(other, Tombola): return AddableBingoCage(self.inspect() + other.inspect()) #self.inspect()继承自BingoCage,返回当前元素组成的有序元组。 else: return NotImplemented def __iadd__(self, other): if isinstance(other, Tombola): other_iterable = other.inspect() else: try: other_iterable = iter(other) except TypeError: self_cls = type(self).__name__ msg = "right operand in += must be {!r} or an iterable" raise TypeError(msg.format(self_cls)) self.load(other_iterable) return self