zoukankan      html  css  js  c++  java
  • python运算符重载

    python对运算符重载的一些限制

    1.不能重载内置类型的运算符

    2.不能新建运算符,只能重载现有的

    3.某些运算符不能重载:is、and、or、not

    建立某Vector类

     1 from array import array
     2 import reprlib
     3 import math
     4 
     5 
     6 class Vector:
     7     typecode = 'd'
     8     shortcut_names = 'xyzt'
     9 
    10     def __init__(self, components):
    11         self._components = array(self.typecode, components)
    12 
    13     def __iter__(self):
    14         return iter(self._components)
    15 
    16     def __repr__(self):
    17         components = reprlib.repr(self._components)
    18         components = components[components.find('['):-1]
    19         return 'Vector({})'.format(components)
    20 
    21     def __str__(self):
    22         return str(tuple(self))
    23 
    24     def __bytes__(self):
    25         return (bytes([ord(self.typecode)]) + bytes(self._components))
    26 
    27     def __eq__(self, other):
    28         return tuple(self) == tuple(other)
    29 
    30     def __abs__(self):
    31         return math.sqrt(sum(x * x for x in self))
    32 
    33     def __bool__(self):
    34         return bool(abs(self))
    35 
    36     @classmethod
    37     def frombytes(cls, octets):
    38         typecode = chr(octets[0])
    39         memv = memoryview(octets[1:]).cast(typecode)
    40         return cls(memv)
    Vector

    可见Vector新Vector

    重载一元运算符

    -  (__neg__)         一元取负运算符

    +  (__pos__)         一元取正运算符

    ~  (__invert__)      对整数按位取反

         (__abs__)              取绝对值

    实现它们:

        def __neg__(self):                     
            return Vector(-x for x in self)      #创建一个新的Vector实例,把self的每个分量都取反
        
        def __pos__(self):
            return Vector(self)                  #创建一个新的Vector实例,传入self各个分量

    __invert__不实现是因为,计算~v时python会抛出TypeError,且输出详细的错误信息,这符合预期。

    !!注意:x和+x可能不相等,例如

    import decimal
    ctx = decimal.getcontext()      #获取当前全局算术运算的上下文引用
    ctx.prec = 40                   #把算术运算上下文精度设置为40
    one_third = decimal.Decimal('1') / decimal.Decimal('3')  #计算1/3
    print(one_third)
    print(one_third == +one_third)  #
    ctx.prec = 28                   #把精度降低为28,默认精度
    print(one_third == +one_third)
    print(+one_third)
    
    
    0.3333333333333333333333333333333333333333
    True 
    False                                       #one_third == +one_third返回False
    0.3333333333333333333333333333              #小数点后位是28位而不是40位

    重载 + 运算符 

    两个向量加在一起生成一个新向量,如果两个不同长度的Vector实例加一起呢,可以抛出错误,但最好是用0填充,那么可以:

        def __add__(self, other):
            pairs = itertools.zip_longest(self, other, fillvalue=0)
            return Vector(a + b for a, b in pairs)
            #pairs是一个生成器,生成(a, b)形式元组,a来自self,b来自other,0填充

    尝试调用:

    if __name__ == '__main__':
        V1 = Vector([3, 4, 5])
        V2 = V1 + (2, 3, 4, 5)
        print(repr(V2))
        V3 = (2, 3, 4, 5) + V1
        print(repr(V3))
    
    #结果
    Vector([5.0, 7.0, 9.0, 5.0])
    TypeError: can only concatenate tuple (not "Vector") to tuple

    实现的加法可以处理任何数值可迭代对象,但是对调操作数加法就会失败。实际上a + b调用的是a.__add__(b),而b + a自然是调用b.__add__(a)。而(2,3,4,5)显然没有实现这样的加法,如何解决?

    为了支持涉及不同类型的运算,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,并在错误消息中指明操作类型不支持。

    __radd__是__add__的反向版本。那么加法可以这样写:

        def __add__(self, other):
            pairs = itertools.zip_longest(self, other, fillvalue=0)
            return Vector(a + b for a, b in pairs)
        
        def __radd__(self, other):
            return self + other        #__radd__直接委托__add__

    新问题是,如果操作类型是单个数或者‘helloworld’这样的字符串抛出的错误不合理:

        V1 = Vector([3, 4, 5])
        V2 = V1 + 1
    #TypeError: zip_longest argument #2 must support iteration
    
        V1 = Vector([3, 4, 5])
        V2 = V1 + 'abc'
    #TypeError: unsupported operand type(s) for +: 'float' and 'str'

    由于类型不兼容而导致运算符特殊方法无法返回有效的结果,那么应该返回NotImplemented,而不是TypeError。返回NotImplemented时,另一个操作数所属的类型还有机会执行运算,即python会尝试调用反向方法。

        def __add__(self, other):
            try:
                pairs = itertools.zip_longest(self, other, fillvalue=0)
                return Vector(a + b for a, b in pairs)
            except TypeError:
                return NotImplemented
    
        def __radd__(self, other):
            return self + other

    发生TypeError时,解释器尝试调用反向运算符方法,如果操作数是不同的类型,对调之后可能反向运算符可以正确计算。

    重载 * 运算符 

    向量 * x,如果x是数值,就是向量每个分量乘以x。

        def __mul__(self, other):
            return Vector(n * other for n in self)
        
        def __rmul__(self, other):
            return self * other

    问题是:提供不兼容的操作数就会出问题,other需要的参数是数字,传入bool类型,或者fractions.Fraction实例等就会出问题。

     这里采用类型检测,但是不硬编码具体类型,而是检查numbers.Real(因为不可能是复数)抽象基类,这个抽象基类涵盖了所有可能可以参与这个运算的类型

        def __mul__(self, other):
            if isinstance(self, numbers.Real):
                return Vector(n * other for n in self)
            else:
                return NotImplemented
    
        def __rmul__(self, other):
            return self * other

    中缀运算符见表:

    重载比较运算符

    比较运算符区别于+ * / 等:

    (1) 正向反向调用使用的是同一系列方法,规则如表13-2.例如__gt__方法调用反向__lt__方法,并且把参数对调。

    (2)对于==和!=来说,如果反向调用失败,python会比较对象ID,而不抛出TypeError。

    仅仅比较长度以及各个分量显然是不合理的,因该加上类型检查:

        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

    对于!=运算符,从object继承的__ne__方法后备行为满足了需求:定义了__eq__方法且不返回NotImplemented时,__ne__会对__eq__返回的结果取反

    (事实上可能x==y成立不代表x!=y不成立,有时需要定义__ne__方法)

     object继承的__ne__方法即是下面代码的C语言版本:

        def __ne__(self, other):
            eq_result = self == other
            if eq_result is NotImplemented:
                return NotImplemented
            else:
                return not eq_result

    重载增量赋值运算符

     对于Vector类,它是不可变类型,是不能实现 += 这样的就地方法的。

    某可变的类型:

    class B(A):
    
    def __add__(self, other):
        if isinstance(other, A):
            return B(self.某属性+other.某属性)
        else:
            return NotImplemented
    
    def __iadd__(self, other):
        if isinstance(other, A)
            other_iterable = other.某属性
        else:
            try:
                other_iterable = iter(other)
            except TpyeError:
                 #抛出xxx错误
            使用other_iterable更新self
            return self

     以上来自《流畅的python》

  • 相关阅读:
    推自己的镜像到网易云
    supervisord常见问题
    supervisord的配置
    一文解读SDN (转)
    一文解读ZooKeeper (转)
    使用 Docker 和 Nginx 打造高性能的二维码服务 (转)
    一文解读Docker (转)
    一文解读分布式架构 (转)
    一文解读分布式事务 (转)
    一文读懂工业大数据 (转)
  • 原文地址:https://www.cnblogs.com/lht-record/p/10306591.html
Copyright © 2011-2022 走看看