如何重载向量加法运算符+

导语

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

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

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

你们都知道若a和b都是同类型序列,a+b能够实现序列接拼,若a和b都是int或者float等数值类型,a+b会实现数学上的加法,见示例1。spa

#示例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是不一样类型的序列,他们可否接拼成功呢?code

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

对于表达式a+b,为了支持涉及不一样类型的运算,Python解释器会执行如下几步操做。blog

  • 若是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)特殊方法,由于他们都在右操做数上调用。ip

若是a和b是不一样类型的序列,执行示例2的程序,会发生什么呢?rem

#示例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

重载运算符+

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

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

如今咱们尝试定义一个Vector类,见示例3。class

#示例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 __ ,事实上,任何可交换的运算符均可以这么作。

好啦,以上就是就是小编今天分享的内容,但愿对你有用。

公众号:CVpython

专一于分享Python和计算机视觉,快点扫码关注我吧!

相关文章
相关标签/搜索