什麼是反射運算符,其實就是反轉了兩個對象,下面先看一個普通運行符的實現: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,因此結果如上。函數
而反射運行其實就是交換這二者,下面看例子:spa
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。.net
總結起來就是:普通的運算調用的是運算符左邊的方法,而反射運算符調用的是右邊的方法,調用的是誰的方法,誰就爲self。code
這裏有幾點要注意的地方:對象
1.不支持同一個類的實例進行反射運算:blog
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__的優先度更高:ip
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__),由於在右邊找的時候,就意味要使用反射運算了。因此最後獲得了這個結果。get
而後是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)
異或運算符,至關於 ^= 運算符.
歡迎你們交流。
參考資料:戳這裏