Python施加了一些限制,做好了灵活性、可用性和安全性方面的平衡:
• 不能重载内置类型的运算符
• 不能新建运算符,只能重载现有的
• 某些运算符不能重载——is、and、or和not(不过位运算符&、|和~可以) # 一元运算符
-(neg)
一元取负算术运算符。如果x是-2,那么-x == 2。
+(pos)
一元取正算术运算符。通常,x == +x,但也有一些例外。如果好奇,请阅读“x和+x何时不相等”附注栏。
~(invert)
对整数按位取反,定义为~x == -(x+1)。如果x是2,那么~x == -3。
还把内置的abs(…)函数列为一元运算符。它对应的特殊方法是__abs__
支持一元运算符很简单,只需实现相应的特殊方法。这些特殊方法只有一个参数,self。
遵守运算符的一个基本规则:始终返回一个新对象。也就是说,不能修改self,要创建并返回合适类型的新实例。
对-和+来说,结果可能是与self同属一类的实例。多数时候,+最好返回self的副本。abs(…)的结果应该是一个标量。
#示例1:把一元运算符-和+添加到之前的示例中
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) #➋
➊ 为了计算-v,构建一个新Vector实例,把self的每个分量都取反。
➋ 为了计算+v,构建一个新Vector实例,传入self的各个分量。
序列应该支持+运算符(用于拼接),以及*运算符(用于重复复制)。然而,我们将使用向量数学运算实现+和*运算符。
两个欧几里得向量加在一起得到的是一个新向量,它的各个分量是两个向量中相应的分量之和。
#示例1
>>> v1 = Vector([3, 4, 5])
>>> v2 = Vector([6, 7, 8])
>>> v1 + v2
Vector([9.0, 11.0, 13.0])
>>> v1 + v2 == Vector([3+6, 4+7, 5+8])
True
#示例2:Vector.__add__方法,第1版
# 在Vector类中定义
def __add__(self, other):
pairs = itertools.zip_longest(self, other, fillvalue=0.0) # ➊
return Vector(a + b for a, b in pairs) # ➋
➊ pairs是个生成器,它会生成(a, b)形式的元组,其中a来自self,b来自other。如果self和other的长度不同,使用fillvalue填充较短的那个可迭代对象。
➋ 构建一个新Vector实例,使用生成器表达式计算pairs中各个元素的和。
注意__add__返回一个新Vector实例,而没有影响self或other。
实现一元运算符和中缀运算符的特殊方法一定不能修改操作数。使用这些运算符的表达式期待结果是新对象。只有增量赋值表达式可能会修改第一个操作数(self)
还可以把Vector加到元组或任何生成数字的可迭代对象上
#示例3
>>> v1 = Vector([3, 4, 5])
>>> v1 + (10, 20, 30)
Vector([13.0, 24.0, 35.0])
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v1 + v2d
Vector([4.0, 6.0, 5.0])
示例3中的两个加法都能如我们所期待的那样计算,这是因为__add__使用了zip_longest(…),它能处理任何可迭代对象,而且构建新Vector实例的生成器表达式仅仅是把zip_longest(…)生成的值对相加(a + b),因此可以使用任何生成数字元素的可迭代对象。
然而,如果对调操作数,混合类型的加法就会失败。
#示例4:果左操作数是Vector之外的对象,第一版Vector.__add__方法无法处理
>>> v1 = Vector([3, 4, 5])
>>> (10, 20, 30) + v1
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: can only concatenate tuple (not "Vector") to tuple
>>> from vector2d_v3 import Vector2d
>>> v2d = Vector2d(1, 2)
>>> v2d + v1
Traceback (most recent call last):
File "" , line 1, in <module>
TypeError: unsupported operand type(s) for +: 'Vector2d' and 'Vector'
为了支持涉及不同类型的运算,Python为中缀运算符特殊方法提供了特殊的分派机制。对表达式a + b来说,解释器会执行以下几步操作
(1) 如果a有__add__方法,而且返回值不是NotImplemented,调用a.add(b),然后返回结果。
(2) 如果a没有__add__方法,或者调用__add__方法返回NotImplemented,检查b有没有
__radd__方法,如果有,而且没有返回NotImplemented,调用b.radd(a),然后返回结果。
(3) 如果b没有__radd__方法,或者调用__radd__方法返回NotImplemented,抛出TypeError,并在错误消息中指明操作数类型不支持。
《《<