运算符的正确重载


:
这次主要讨论一下几个问题
• Python如何处理中缀运算符中不同类型的操作数
• 使用鸭子类型或显式类型检查处理不同类型的操作数
• 中缀运算符如何表明自己无法处理操作数
• 众多比较运算符(如==、>、<=,等等)的特殊行为
• 增量赋值运算符(如+=)的默认处理方式和重载方式

运算符重载基础

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 "<stdin>", 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 "<stdin>", 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,并在错误消息中指明操作数类型不支持。
《《<在这里插入图片描述

使用__add__和__radd__计算a + b的流程图
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值