反射運算
什么是反射運算符,其實就是反轉了兩個對象,下面先看一個普通運行符的實現:
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)
-
異或運算符,相當於 ^= 運算符.
歡迎大家交流。
參考資料:戳這里