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)

  • 異或運算符,相當於 ^= 運算符.

 


  歡迎大家交流。

  參考資料:戳這里

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM