• 流畅的python学习笔记:第十三章:重载运算符__add__,__iadd__,__radd__,__mul__,__rmul__,__neg__,__eq__,__invert__,__pos__


    在前面第十章以及第一章的时候介绍了Vector对象的运算符重载。第十三章专门介绍运算符重载。这里我们看几个之前没讲过的运算符__neg__,__pos__,__invert__
    class Vector(object):
        def __init__(self,x):
            self.x=x
        def __neg__(self):
            return "Vector(%d)" % (-self.x)
        def __str__(self):
            return "Vector(%s)" % (str(self.data))
        def __iter__(self):
            return iter(self.data)
        def __pos__(self):
            return "Vector(%d)" % (self.x+1)
        def __invert__(self):
            return "Vector(%d)" % (~self.x)

    if __name__=="__main__":
        v=Vector(1)
        print -v
        print +v
        print ~v
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    Vector(-1)
    Vector(2)
    Vector(-2)
    __neg__是在-v的时候调用
    __pos__是在+v的时候调用
    __invert__是在~v的时候调用
    下面我们重新来看下+运算符。我们想实现两个向量想加Vector([1,2,3])+Vector([1,2,3])得到[2,4,6].代码如下
    class Vector(object):
        def __init__(self,x):
            self.x=x
        def __add__(self, other):
            return [a+b for a,b in zip(self.x,other.x)]
    if __name__=="__main__":
        v1=Vector([1,2,3])
        v2=Vector([1,2,3])
        print v1+v2
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    [2, 4, 6]
    在前面的例子中通过zip函数对两个列表进行配对得到元组。[(1,1),(2,2),(3,3)]然后迭代进行加法运算最终得到结果[2,4,6]
    但是如果两个向量的格式变化一下。变成Vector([1,2,3])+Vector([1,2,3,4]).我们期望得到Vector([2,4,,6,4]), 向量Vector([1,2,3,4])由于比Vector([1,2,3])多一个数字,想加的时候最好是用零填充较短的那个向量。那么实际结果如何呢
    if __name__=="__main__":
        v1=Vector([1,2,3])
        v2=Vector([1,2,3,4])
        print v1+v2
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    [2, 4, 6]
    我们看到结果和上次一样还是[2,4,6]和我们预想的[2,4,6,4]相差太大。原因在于zip得到的结果是[(1, 1), (2, 2), (3, 3)],并没有包含多出来的4。这种情况下我们就需要用到itertools.izip_lonest,这个方法会自动填充缺失的向量元素
    class Vector(object):
        def __init__(self,x):
            self.x=x
        def __add__(self, other):
            pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
            return [a+b for a,b in pairs]
    if __name__=="__main__":
        v1=Vector([1,2,3])
        v2=Vector([1,2,3,4])
        print v1+v2
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    [2, 4, 6, 4]
    izip_longest中,fillvalue代表填充的值。本次例子中设置的是0.因此最后一位是0+4=4
     
    接下来我们的需求继续变下。我们想实现Vector([1,2,3])+[1,2,3]也就是向量类和列表相加
    class Vector(object):
        def __init__(self,x):
            self.x=x
        def __add__(self, other):
            if type(other) == list:
                pairs=itertools.izip_longest(self.x,other,fillvalue=0)
            else:
                pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
            return [a+b for a,b in pairs]
    if __name__=="__main__":
        v1=Vector([1,2,3])
        v2=Vector([1,2,3,4])
        print v1+[1,2,3,4]
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    [2, 4, 6, 4]
    首先在__add__中判断other的属性,然后针对性的得到pairs
     
    那如果是[1,2,3,4]+v1的结果会是什么呢?运行报错。
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    Traceback (most recent call last):
      File "E:/py_prj/fluent_python/chapter13.py", line 27, in <module>
        print [1,2,3,4]+v1
    TypeError: can only concatenate list (not "Vector") to list
    这是因为[1,2,3,4]并没有add方法,只有Vector才有,这种场景需要用到__radd__方法
    具体的运算流程图如下:
    1 如果a有__add__方法,则进行a.__add__(b)运算,否则返回Notimplemented然后检查b有没有__radd__方法。如果有,则调用b.__radd__(a),否则返回Notimplemented

    我们在Vector中增加__radd__方法:
    def __radd__(self, other):
        return self+other
    __radd__直接委托__add__进行运算。代码也很简单。这下运算结果正确了。
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    [2, 4, 6, 4]
    同样的方法也适用于乘法运算符__mul__和__rmul__。
     
    下面来看下比较运算符以及反向比较在__eq__的作用。
    首先定义__eq__的代码,首先比较长度,然后比较对应的元素,如果都相等则返回True,否则返回False
    def __eq__(self, other):
        if type(other) == list or type(other)==tuple:
            pairs=itertools.izip_longest(self.x,other,fillvalue=0)
            length=len(other)
        else:
            pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
            length=len(other.x)
        return (len(self.x)==length) and all(a==b for a,b in pairs)
    if __name__=="__main__":
        v1=Vector([1.0,2.0,3.0])
        v2=Vector(range(1,4))
        print 'v1 compare v2 %s' % (v1==v2)
        v3=(1,2,3)
        print 'v1 compare v3 %s' % (v1==v3)
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    v1 compare v2 True
    v1 compare v3 True 
    v1和v2相等这个没啥疑问,但是当用元组(1,2,3)和向量v1相比的时候,得到的结果也是True,我们如果用[1,2]==(1,2)得到的结果为False,因为列表和元组不是一个类型。那么为什么向量和元组相比却是相等的呢? 这就是问题所在了。在比较的时候并没有比较类型。代码更新如下:
    def __eq__(self, other):
        if type(other) == list or type(other)==tuple:
            pairs=itertools.izip_longest(self.x,other,fillvalue=0)
            length=len(other)
        else:
            pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
            length=len(other.x)
        if isinstance(other,Vector): #在这里判断是否属于向量类
            return (len(self.x)==length) and all(a==b for a,b in pairs)
        else:
            return NotImplemented
    再继续看一个例子,增加一个Vector2d向量:
    class Vector2d(object):
        def __init__(self,x):
            self.x=x
        def __eq__(self, other):
            return tuple(self.x) ==  tuple(other.x)
    if __name__=="__main__":
        v1=Vector([1.0,2.0,3.0])
     
    v4=Vector2d([1,2,3])
    print v1==v4
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    True
    得到的结果为True. Vector2d不属于Vector的实例呢。为什么会相等呢。这里用到了反向比较的方法
    (1)首先Vector.__eq__(v1,v4),v4不是Vector的实例。返回Notimplemented。然后尝试调用Vector2d.__eq__(v4.v1)
    (2)Vector2d.__eq__(v4.v1)把两个操作数都变成元组。然后比较。结果为True。
    其实在用Vector和元组进行比较的时候,也是一样的首先Vector.__eq__(v1,v4),返回Notimplemented。接着开始调用tuple.__eq__(v3,v1),但是tuple不知道vector是什么,因此返回Notimplemented。对于==来说,如果反向调用也是Notimplemented,则会进行最后的尝试,比较对象的ID,如果仍然不相等则返回False。
     
    接下来我们看下!=运算符
    print v1!=v4
    得到的结果是True,但是我们并没有实现__neq__的方法,也能得到对应的结果,其实__neq__不用实现,只要定义了__eq__的方法,__neq__则会自动对__eq__的结果取反。
     
    最后来看下增量赋值运算符:
    if __name__=="__main__":
        v1=Vector([1.0,2.0,3.0])
        print id(v1)⑴
        v2=Vector(range(1,4))
        v1+=v2⑵
        print v1
        print id(v1)⑶
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    25012400
    [2.0, 4.0, 6.0]
    25005672
    (1)中,得到的id(v1)=25012400。 
    (2)中,经过v1+=v2后。在(3)中打印id(v1)等于25005672.和之前第一步的不一样了,也就是创建了新的实例。
    如果我们想实现就地运算,不生成新的实例,就在v1的基础上想加呢。这就需要实现__iadd__方法。当执行+=的时候会自动调用__iadd__方法,而如果没有实现__iadd__方法,则会自动调用__add__方法
    def __iadd__(self, other):
        if type(other) == list:
            pairs=itertools.izip_longest(self.x,other,fillvalue=0)
        else:
            pairs=itertools.izip_longest(self.x,other.x,fillvalue=0)
        i=0
        for p in pairs:
            self.x[i]=p[0]+p[1]
            i+=1
        return self
    __iadd__方法中,返回的是self. 再来看下执行结果:
    E:python2.7.11python.exe E:/py_prj/fluent_python/chapter13.py
    25274544
    Vector([2.0, 4.0, 6.0])
    25274544
    得到的实例是同一样
    
    
  • 相关阅读:
    Eugene and an array CodeForces
    Kind Anton CodeForces
    浙江大学PAT上机题解析之1011. World Cup Betting (20)
    九度OnlineJudge之1001:A+B for Matrices
    九度OnlineJudge之1468:Sharing
    九度OnlineJudge之1464:Hello World for U
    C++ STL 学习笔记
    浙江大学PAT上机题解析之1009. Product of Polynomials (25)
    浙江大学PAT上机题解析之1050. String Subtraction (20)
    浙江大学PAT上机题解析之1008. Elevator (20)
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/7227452.html
Copyright © 2020-2023  润新知