python魔法方法-反射運算和增量運算

反射運算

  什麼是反射運算符,其實就是反轉了兩個對象,下面先看一個普通運行符的實現: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)

  • 異或運算符,至關於 ^= 運算符.

 


  歡迎你們交流。

  參考資料:戳這裏

相關文章
相關標籤/搜索