快捷搜索:  汽车  科技

向量加法运算方法(如何重载向量加法运算符)

向量加法运算方法(如何重载向量加法运算符)#示例2 a = (1 2) b = [3 4] print(a b)首先解释器会调用a.__ add (b)方法,因为a和b是不同类型的序列,所以返回NotImplemented。然后解释器检查b是否有b. radd __ (a) 方法,然后调用该方法,但是还是返回NotImplemented,最终抛出TypeError结果。如果a和b是不同类型的序列,执行示例2的程序,会发生什么呢?要回答这个问题,我们先了解一下Python为中缀运算符特殊方法提供的特殊分派机制,其流程见下图。对于表达式a b,为了支持涉及不同类型的运算,Python解释器会执行以下几步操作。radd是 __ add __的“反向”版本,Alex、Anna和Leo几位技术大佬喜欢称之为“右向”(right)特殊方法,因为他们都在右操作数上调用。

导语

小编一直都觉自己公众号的排版很鸡肋,从这篇文章开始将使用了新的排版风格,还特意地做了一个卡通二维码(见文末),希望大家会喜欢(不要脸地假装有很多粉丝)。其实关于排版,小编要真心感谢一下景禹大佬的指导。好了,今天想跟大家谈谈如何重载运算符 ,认真看完这篇文章,你将收获:

  • 了解中缀运算符特殊方法的分派机制
  • 了解向量类如何实现 add 方法
  • 了解向量类如何实现 radd 方法

a b背后如何调用特殊方法

大家都知道若a和b都是同类型序列,a b可以实现序列接拼,若a和b都是int或者float等数值类型,a b会实现数学上的加法,见示例1。

#示例1 a = (1 2) b = (4 5) print(a b) #(1 2 4 5) a = [1 2] b = [4 5] print(a b) #[1 2 4 5] a = b = 1 print(a b) #2 a = b = 1.0 print(a b) #2.0

但是,如果a和b是不同类型的序列,他们能否接拼成功呢?

要回答这个问题,我们先了解一下Python为中缀运算符特殊方法提供的特殊分派机制,其流程见下图。

向量加法运算方法(如何重载向量加法运算符)(1)

对于表达式a b,为了支持涉及不同类型的运算,Python解释器会执行以下几步操作。

  • 如果a有__ add 方法,返回值不是NotImplemented,调用a. add __(b),然后返回结果
  • 如果a没有__ add 方法,或者调用 add 方法返回NotImplemented,检查b有没有 radd 方法,如果有,调用b. radd __(a)方法后没有返回NotImplemented,返回结果。
  • 如果b没有__ radd 方法,或者调用 radd __方法返回NotImplemented,抛出TypeError,并在错误消息中指明操作类型不支持。

radd是 __ add __的“反向”版本,Alex、Anna和Leo几位技术大佬喜欢称之为“右向”(right)特殊方法,因为他们都在右操作数上调用。

如果a和b是不同类型的序列,执行示例2的程序,会发生什么呢?

#示例2 a = (1 2) b = [3 4] print(a b)

首先解释器会调用a.__ add (b)方法,因为a和b是不同类型的序列,所以返回NotImplemented。然后解释器检查b是否有b. radd __ (a) 方法,然后调用该方法,但是还是返回NotImplemented,最终抛出TypeError结果。

TypeError: can only concatenate tuple (not "list") to tuple

重载运算符

关于重载运算符,有几点需要注意一下的:

  • 不能重载内置类型的运算符,即list等这些内置类型的运算符不能重载。
  • 不能新建运算符,只能重载现有的。
  • 有些运算符不能重载,is、and、or和not(位运算&、|和~可以)。

现在我们尝试定义一个Vector类,见示例3。

#示例3 class Vector(object): def __init__(self components): self.components = list(components)

如果我们不对Vector重载运算符 ,两个Vector类相加抛出错误,见示例4

#示例4 v1 = Vector([1 2 3]) v2 = Vector([1 2 3]) print(v1 v2) #TypeError: unsupported operand type(s) for : 'Vector' and 'Vector'

好了,我们现在马上对Vector重载运算符 吧,见示例5,但是我们不是实现接拼,而是实现数学上的加法,因为对于一个向量,接拼功能意义不大。

#示例5 class Vector(object): def __init__(self components): self.components = list(components) def __iter__(self): return iter(self.components) def __str__(self): return str(self.components) def __add__(self others): print("__add__") try: pairs = itertools.zip_longest(self others fillvalue=0.0) return Vector(a b for a b in pairs) except TypeError: return NotImplemented v1 = Vector([1 2 3]) v2 = Vector([1 2 3]) v3 = [1 2 3] print(v1 v2) print(v1 v3)

输出结果:

__add__ [2 4 6] __add__ [2 4 6]

从示例5中可以看到,解析器调用了__ add __方法实现了两个Vector或者Vector和具有数值元素的可迭代类型的相加。示例5能够实现V1 V3,那是否能实现V3 v1呢?见示例6。

#示例6 print(V3 V1) #TypeError: can only concatenate list (not "Vector") to list

示例6抛出错误了,当解释器执行v3.__ add (V1)时候,因为调用的是列表的 add 方法,不能与自定义的类型相加,所以返回结果NotImplemented,并尝试执行V1. radd (V3)方法,但是在自定义的Vector类并没有实现该方法,所以最后还是抛出了TypeError。好了,现在尝试实现Vector类的 radd __ 方法。在示例5的基础上添加示例7的代码。

#示例7 def __radd__(self other): print("__radd__") return self other

再次运行示例6,得到如下输出结果。

__radd__ __add__ [2 4 6]

当解释器执行V3.__ add (V1)返回NotImplemented之后,就会调用自定义类中的 radd 方法, radd 方法把计算结果委托给 add __ ,事实上,任何可交换的运算符都可以这么做。

猜您喜欢: