zoukankan      html  css  js  c++  java
  • python魔法方法-反射运算和增量运算

    反射运算

      什么是反射运算符,其实就是反转了两个对象,下面先看一个普通运行符的实现:

    class Foo(object):
        def __init__(self, x):
            self.x = x
    
        def __add__(self, other):
            return 'Foo:%s + %s' % (self.x, other.x)
    
    
    class Boo(object):
        def __init__(self, x):
            self.x = x
    
        def __add__(self, other):
            return 'Boo:%s + %s' % (self.x, other.x)
    
    
    a = Foo(123)
    b = Boo(321)
    print a + b
    print b + a

     

      在普通的加法运算中,调用的是+号左边的__add__方法,调用谁谁就为self。所以左边是self,右边为other,所以结果如上。

      而反射运行其实就是交换这两者,下面看例子:

    class Foo(object):
        def __init__(self, x):
            self.x = x
    
        def __radd__(self, other):
            return 'Foo:%s + %s' % (self.x, other.x)
    
    
    class Boo(object):
        def __init__(self, x):
            self.x = x
    
        def __radd__(self, other):
            return 'Boo:%s + %s' % (self.x, other.x)
    
    
    a = Foo(123)
    b = Boo(321)
    print a + b
    print b + a

      首先,不同的地方是这里调用的+后右边的__radd__方法。然后本来是左边的为self的,现在变成了右边的为self。

      总结起来就是:普通的运算调用的是运算符左边的方法,而反射运算符调用的是右边的方法,调用的是谁的方法,谁就为self。

      这里有几点要注意的地方:

    1.不支持同一个类的实例进行反射运算:

    class Foo(object):
        def __init__(self, x):
            self.x = x
    
        def __radd__(self, other):
            return 'Foo:%s + %s' % (self.x, other.x)
    
    a = Foo(123)
    b = Foo(321)
    print a + b
    print b + a

    2.当一个类实现了__add__的时候,将会掩盖__radd__方法,也就是__add__的优先度更高:

    class Foo(object):
        def __init__(self, x):
            self.x = x
    
        def __radd__(self, other):
            return 'Foo:%s + %s' % (self.x, other.x)
    
    
    class Boo(object):
        def __init__(self, x):
            self.x = x
    
        def __add__(self, other):
            return 'Boo add:%s + %s' % (self.x, other.x)
    
        def __radd__(self, other):
            return 'Boo radd:%s + %s' % (self.x, other.x)
    
    
    a = Foo(123)
    b = Boo(321)
    print a + b
    print b + a

    其逻辑如下:

      首先a + b,python看到了 a 中没有 __add__方法(忽略了__radd__),就去 b 中找__radd__(而不是__add__),因为在右边找的时候,就意味要使用反射运算了。所以最后得到了这个结果

      然后是b + a,python看到了 b 中有 __add__方法,就直接调用了它,不管 a 的内部是如何的。

     基本反射运算就是这么一回事,下面是一些总结:

    • __radd__(self, other)

    • 反射加法

    • __rsub__(self, other)

    • 反射减法的

    • __rmul__(self, other)

    • 反射除法

    • __rfloordiv__(self, other)

    • 反射地板除,使用//运算符的

    • __rdiv__(self, other)

    • 反射除法,使用/运算符的.

    • __rtruediv__(self, other)

    • 反射真除.注意只有from __future__ import division 的时候它才有效

    • __rmod__(self, other)

    • 反射取模运算,使用%运算符.

    • __rdivmod__(self, other)

    • 长除法,使用divmod()内置函数,当divmod(other,self)时被调用.

    • __rpow__

    • 反射乘方,使用**运算符的

    • __rlshift__(self, other)

    • 反射左移,使用<<操作符.

    • __rrshift__(self, other)

    • 反射右移,使用>>操作符.

    • __rand__(self, other)

    • 反射位与,使用&操作符.

    • __ror__(self, other)

    • 反射位或,使用|操作符.

    • __rxor__(self, other)

    • 反射异或,使用^操作符.

    增量运算

       所谓的增量运算,其实就是 x += 1 这样的形式,下面是几个例子:

    class Foo(object):
        def __init__(self, x):
            self.x = x
    
        def __iadd__(self, other):
            return 'Foo iadd: %s + %s' % (self.x, other)
    
    a = Foo(123)
    a += 1
    print a

      但是,如果两个对象的实现了__iadd__,情况就会大为不同:

    class Foo(object):
        def __init__(self, x):
            self.x = x
    
        def __iadd__(self, other):
            return 'Foo iadd: %s + %s' % (self.x, other.x)
    
    class Boo(object):
        def __init__(self, x):
            self.x = x
    
        def __iadd__(self, other):
            return 'Boo iadd: %s + %s' % (self.x, other.x)
    
    
    a = Foo(123)
    b = Boo(321)
    a += b
    print a

      看似很正常,然而代码如下时:

    a = Foo(123)
    b = Boo(321)
    a += b
    print a
    b += a
    print b

     

      报错显示:str没有x这个属性,但是按照代码来看,两个对象都有x属性呀。

      在b += a 这行有错,也就是说self为 b,other为 a。后来试验了一番,发现将:

       return 'Boo iadd: %s + %s' % (self.x, other.x) 

      改为:

       return 'Boo iadd: %s + %s' % (self.x, other) 

      代码就不会报错了,但是输出几个如下:

      很奇怪,other变成了a中__iadd__的返回值了,也就是说当a调用了__iadd__方法之后,在将其用在其他的增量运算时,other不在代表a对象本身,而是其__iadd__的返回值。

      当我们回归其本质:x += 1 ==> x = x + 1 可以看出,x 其实进行了重新赋值,重新赋值成了 __iadd__ 的返回值。而我们代码示例中,这个方法的返回值是一个字符串。在一开始时,x是我们类的实例。但是在进行了增量运算后,x 变成了魔法方法的返回值,也就是字符串了,所以才会出现以上的报错。

      所以我们在使用的时候要注意 x 身份的改变,不然会有许多意想不到的麻烦。

    关于增量方法的总结:

    • __iadd__(self, other)

    • 加法赋值

    • __isub__(self, other)

    • 减法赋值.

    • __imul__(self, other)

    • 乘法赋值

    • __ifloordiv__(self, other)

    • 整除赋值,地板除,相当于 //= 运算符.

    • __idiv__(self, other)

    • 除法赋值,相当于 /= 运算符.

    • __itruediv__(self, other)

    • 真除赋值,注意只有你 whenfrom __future__ import divisionis,才有效.

    • __imod_(self, other)

    • 模赋值,相当于 %= 运算符.

    • __ipow__

    • 乘方赋值,相当于 **= 运算符.

    • __ilshift__(self, other)

    • 左移赋值,相当于 <<= 运算符.

    • __irshift__(self, other)

    • 左移赋值,相当于 >>= 运算符.

    • __iand__(self, other)

    • 与赋值,相当于 &= 运算符.

    • __ior__(self, other)

    • 或赋值,相当于 |= 运算符.

    • __ixor__(self, other)

    • 异或运算符,相当于 ^= 运算符.


      欢迎大家交流。

      参考资料:戳这里

  • 相关阅读:
    ASP.NET Core 2.2 : 二十七. JWT与用户授权(细化到Action)
    ASP.NET Core 2.2 : 二十六. 应用JWT进行用户认证及Token的刷新
    ASP.NET Core 发布到Linux需要注意的地方
    小程序根据数字做for循环
    Visual Studio 2019 正式版 更新内容
    CodeSmith 二、多模板按目录树批量自动生成代码
    CodeSmith 一、连接Mysql
    ASP.NET Core 2.2 十九. Action参数的映射与模型绑定
    ASP.NET Core 2.2 十八.各种Filter的内部处理机制及执行顺序
    ASP.NET Core 2.2 : 十七.Action的执行(Endpoint.RequestDelegate后面的故事)
  • 原文地址:https://www.cnblogs.com/scolia/p/5686267.html
Copyright © 2011-2022 走看看