反射运算
什么是反射运算符,其实就是反转了两个对象,下面先看一个普通运行符的实现:
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'
改为:
return 'Boo iadd: %s + %s'
代码就不会报错了,但是输出几个如下:
很奇怪,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)
- 异或运算符,相当于 ^= 运算符.
欢迎大家交流。