python支持什么样的增量赋值操作符_众多比较运算符和增量赋值运算符

众多比较运算符和增量赋值运算符

众多比较运算符

Python解释器对众多比较运算符(==、!=、>、=、<=)的处理与前文类似,不过在两个方面有重大区别。

• 正向和反向调用使用的是同一系列方法。这方面的规则如表13-2所示。例如,对==来说,正向和反向调用都是__eq__方法,只是把参数对调了;而正向的__gt__方法调用的是反向的__lt__方法,并把参数对调。

• 对==和!=来说,如果反向调用失败,Python会比较对象的ID,而不抛出TypeError。

bbc696f24334c17630005f59ddf25236.png

现在Python 3返回结果是对__eq__结果的取反。对于排序比较运算符,Python 3抛出TypeError,并把错误消息设为’unorderable types: int() < tuple()’。

来分析并改进Vector.__eq__方法的行为,之前是这样定义的

#示例1

class Vector:

# 省略了很多行

def __eq__(self, other):

return (len(self) == len(other) and

all(a == b for a, b in zip(self, other)))

这个方法的行为如示例2

#示例2

>>> va = Vector([1.0, 2.0, 3.0])

>>> vb = Vector(range(1, 4))

>>> va == vb # ➊

True

>>> vc = Vector([1, 2])

>>> from vector2d_v3 import Vector2d

>>> v2d = Vector2d(1, 2)

>>> vc == v2d # ➋

True

>>> t3 = (1, 2, 3)

>>> va == t3 # ➌

True

➊ 两个具有相同数值分量的Vector实例是相等的。

➋ 如果Vector实例的分量与Vector2d实例的分量都相等,那么两个实例相等。

➌ Vector实例的分量与元组或其他任何可迭代对象的元素相等,那么对象也相等。

示例示例2中的最后一个结果可能不是很理想

#示例3:改进Vector类的__eq__方法

def __eq__(self, other):

if isinstance(other, Vector): #➊

return (len(self) == len(other) and

all(a == b for a, b in zip(self, other)))

else:

return NotImplemented #➋

➊ 如果other操作数是Vector实例(或者Vector子类的实例),那就像之前那样比较。

➋ 否则,返回NotImplemented。

#示例4

>>> va = Vector([1.0, 2.0, 3.0])

>>> vb = Vector(range(1, 4))

>>> va == vb # ➊

True

>>> vc = Vector([1, 2])

>>> from vector2d_v3 import Vector2d

>>> v2d = Vector2d(1, 2)

>>> vc == v2d # ➋

True

>>> t3 = (1, 2, 3)

>>> va == t3 # ➌

False

➊ 结果与之前一样,与预期相符。

➋ 结果与之前一样,但是为什么呢?稍后解释。8

➌ 结果不同了,这才是我们想要的。但是为什么会这样?请往下读……

这是因为示例3中的__eq__方法返回了NotImplemented。Vector实例与Vector2d实例比较时,具体步骤如下。

(1) 为了计算vc == v2d,Python调用Vector.eq(vc, v2d)。

(2) 经Vector.eq(vc, v2d)确认,v2d不是Vector实例,因此返回NotImplemented。

(3) Python得到NotImplemented结果,尝试调用Vector2d.eq(v2d, vc)。

(4) Vector2d.eq(v2d, vc)把两个操作数都变成元组,然后比较,结果是True

在示例4中,Vector实例和元组比较时,具体步骤如下。

(1) 为了计算va == t3,Python调用Vector.eq(va, t3)。

(2) 经Vector.eq(va, t3)确认,t3不是Vector实例,因此返回NotImplemented。

(3) Python得到NotImplemented结果,尝试调用tuple.eq(t3, va)。

(4) tuple.eq(t3, va)不知道Vector是什么,因此返回NotImplemented。

(5) 对==来说,如果反向调用返回NotImplemented,Python会比较对象的ID,作最后一搏。

增量赋值运算符

Vector类已经支持增量赋值运算符+=和*=了,如示例5所示。

#示例5:增量赋值不会修改不可变目标,而是新建实例,然后重新绑定

>>> v1 = Vector([1, 2, 3])

>>> v1_alias = v1 # ➊

>>>> id(v1) # ➋

4302860128

>>> v1 += Vector([4, 5, 6]) # ➌

>>> v1 # ➍

Vector([5.0, 7.0, 9.0])

>>> id(v1) # ➎

4302859904

>>> v1_alias # ➏

Vector([1.0, 2.0, 3.0])

>>> v1 *= 11 # ➐

>>> v1 # ➑

Vector([55.0, 77.0, 99.0])

>>> id(v1)

4302858336

➊ 复制一份,供后面审查Vector([1, 2, 3])对象。

➋ 记住一开始绑定给v1的Vector实例的ID。

➌ 增量加法运算。

➍ 结果与预期相符……

➎ ……但是创建了新的Vector实例。

➏ 审查v1_alias,确认原来的Vector实例没被修改。

➐ 增量乘法运算。

➑ 同样,结果与预期相符,但是创建了新的Vector实例。

如果实现了就地运算符方法,例如__iadd__,计算a += b的结果时会调用就地运算符方法。这种运算符的名称表明,它们会就地修改左操作数,而不会创建新对象作为结果。

#示例6:使用+运算符新建AddableBingoCage实例

>>> vowels = 'AEIOU'

>>> globe = AddableBingoCage(vowels) #➊

>>> globe.inspect()

('A', 'E', 'I', 'O', 'U')

>>> globe.pick() in vowels #➋

True

>>> len(globe.inspect()) #➌

4

>>> globe2 = AddableBingoCage('XYZ') #➍

>>>> globe3 = globe + globe2

>>> len(globe3.inspect()) #➎

7

>>> void = globe + [10, 20] #➏

Traceback (most recent call last):

...

TypeError: unsupported operand type(s) for +: 'AddableBingoCage' and 'list'

➊ 使用5个元素(vowels中的各个字母)创建一个globe实例。

➋ 从中取出一个元素,确认它在vowels中。

➌ 确认globe的元素数量减少到4个了。

➍ 创建第二个实例,它有3个元素。

➎ 把前两个实例加在一起,创建第3个实例。这个实例有7个元素。

➏ AddableBingoCage实例无法与列表相加,抛出TypeError。那个错误消息是__add__方

法返回NotImplemented时Python解释器输出的。

#示例7:可以使用+=运算符载入现有的AddableBingoCage实例(接续示例13-16)

>>> globe_orig = globe ➊

>>> len(globe.inspect()) ➋

4

>>> globe += globe2 ➌

>>> len(globe.inspect())

7

>>> globe += ['M', 'N'] ➍

>>> len(globe.inspect())

9

>>> globe is globe_orig ➎

True

>>> globe += 1 ➏

Traceback (most recent call last):

...

TypeError: right operand in += must be 'AddableBingoCage' or an iterable

➊ 复制一份,供后面检查对象的标识。

➋ 现在globe有4个元素。

➌ AddableBingoCage实例可以从同属一类的其他实例那里接受元素。

➍ +=的右操作数也可以是任何可迭代对象。

➎ 在这个示例中,globe始终指代globe_orig对象。

➏ AddableBingoCage实例不能与非可迭代对象相加,错误消息会指明原因。

与+相比,+=运算符对第二个操作数更宽容。+运算符的两个操作数必须是相同类型(这里是AddableBingoCage),如若不然,结果的类型可能让人摸不着头脑。而+=的情况更明确,因为就地修改左操作数,所以结果的类型是确定的。

通过观察内置list类型的工作方式,我确定了要对+和+=的行为做什么限制。my_list + x只能用于把两个列表加到一起,而my_list += x可以使用右边可迭代对象x中的元素扩展左边的列表。list.extend()的行为也是这样的,它的参数可以是任何可迭代对象。

一般来说,如果中缀运算符的正向方法(如__mul__)只处理与self属于同一类型的操作数,那就无需实现对应的反向方法(如__rmul__),因为按照定义,反向方法是为了处理类型不同的操作数。

原文链接:https://blog.csdn.net/weixin_38492159/article/details/107715522

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值