1. 程式人生 > 實用技巧 >第043講:魔法方法:算術運算2

第043講:魔法方法:算術運算2

課題筆記:

例子:

>>> class int(int):
    def __add__(self, other):
        return int.__sub__(self, other)

    
>>> a = int('5')
>>> a
5
>>> b = int('3')
>>> a + b
2
>>> 

反運算魔方方法,與算術運算子保持一一對應,不同之處就是反運算的魔法方法多了一個“r”。Python學習:反運算 來自:,連結:

https://blog.csdn.net/nanhuaibeian/article/details/86687806

>>> class Nint(int):
    def __radd__(self, other):
        return int.__sub__(self, other)

    
>>> a = Nint(5)
>>> b = Nint(3)
>>> a + b
8
>>> 1 + b  # 這邊self = b, other是1, 所以返回了b - 1
2
>>> #注意這裡1 和 b  兩個型別不一樣!

解釋:反運算在運算子左右型別不同,且左邊沒用相應運算方法時被呼叫。官方文件:右運算元為左運算元的子類時,優先呼叫子類的反運算方法。

在舉個反例:

>>> class Nint(int):
    def __rsub__(self, other):
        return int.__sub__(self, other)

    
>>> a = Nint(5)
>>> 3 - a
2

這裡3沒有sub,所以去找a的rsub,__rsub__(self, other)這裡self是a,other是3;呼叫sub就變成了a - 3了,所以要實現3 - a的話要在sub括號裡對調兩個引數。

找不到a的方法是因為a後面沒有運算子。

增量運算子

a = a + b就是 a += b

一元操作符

-a,取相反數的意思

~a,按位取反

測試題:

0. 物件相加(a + b),如果 a 物件有 __add__ 方法,請問 b 物件的 __radd__ 會被呼叫嗎?

不會,實驗如下:

>>> class Nint(int):
        def __radd__(self, other):
                print("__radd__ 被呼叫了!")
                return int.__add__(self, other)

>>> a = Nint(5)
>>> b = Nint(3)
>>> a + b
8
>>> 1 + b
__radd__ 被呼叫了!
4

1. Python 什麼時候會呼叫到反運算的魔法方法?

當右邊找不到相應方法的時候。

答:例如 a + b,如果 a 物件的 __add__ 方法沒有實現或者不支援相應的操作,那麼 Python 就會自動呼叫 b 的 __radd__ 方法。

2. 請問如何在繼承的類中呼叫基類的方法?

基類.__XX__(),就是呼叫基類的魔法方法

答:使用 super() 這個 BIF 函式。

class Derived(Base):
    def meth (self):
        super(Derived, self).meth()

3. 如果我要繼承的基類是動態的(有時候是 A,有時候是 B),我應該如何部署我的程式碼,以便基類可以隨意改變。

基類用一個變數代替嗎?

答:你可以先為基類定義一個別名,在類定義的時候,使用別名代替你要繼承的基類。如此,當你想要改變基類的時候,只需要修改給別名賦值的那個語句即可。順便說一下,當你的資源是視情況而定的時候,這個小技巧很管用。

例子:

BaseAlias = BaseClass  # 為基類取別名

class Derived(BaseAlias):
    def meth(self):
        BaseAlias.meth(self)  # 通過別名訪問基類
        ...

4. 嘗試自己舉一個例子說明如何使用類的靜態屬性。(一定要自己先動手再看答案哦^_^)

類.XX屬性,呼叫吧

答:類的靜態屬性很簡單,在類中直接定義的變數(沒有 self.)就是靜態屬性。引用類的靜態屬性使用”類名.屬性名”的形式。

類的靜態屬性應用(計算該類被例項化的次數):

class C:
    count = 0  # 靜態屬性

    def __init__(self):
        C.count = C.count + 1  # 類名.屬性名的形式引用

    def getCount(self):
        return C.count

5. 嘗試自己舉例說明如何使用類的靜態方法,並指出使用類的靜態方法有何有點和需要注意的地方?(一定要自己先動手再看答案哦^_^)

類的屬性名和類的方法名要注意不能同名。

答:靜態方法類的特殊方法,靜態方法只需要在普通方法的前邊加上 @staticmethod 修飾符即可。

class C:
        @staticmethod  # 該修飾符表示 static() 是靜態方法
        def static(arg1, arg2, arg3):
                print(arg1, arg2, arg3, arg1 + arg2 + arg3)

        def nostatic(self):
                print("I'm the f**king normal method!")

靜態方法最大的優點是:不會繫結到例項物件上,換而言之就是節省開銷。

>>> c1 = C()
>>> c2 = C()
# 靜態方法只在記憶體中生成一個,節省開銷
>>> c1.static is C.static
True
>>> c1.nostatic is C.nostatic
False
>>> c1.static
<function C.static at 0x03001420>
>>> c2.static
<function C.static at 0x03001420>
>>> C.static
<function C.static at 0x03001420>
# 普通方法每個例項物件都擁有獨立的一個,開銷較大
>>> c1.nostatic
<bound method C.nostatic of <__main__.C object at 0x03010590>>
>>> c2.nostatic
<bound method C.nostatic of <__main__.C object at 0x032809D0>>
>>> C.nostatic
<function C.nostatic at 0x0328D2B8>

使用的時候需要注意的地方:靜態方法不需要 self 引數,因此即使是使用物件去訪問self 引數也不會傳進去

>>> c1.static(1, 2, 3)
1 2 3 6
>>> C.static(1, 2, 3)
1 2 3 6

動動手:

0. 定義一個類,當例項化該類的時候,自動判斷傳入了多少個引數,並顯示出來:

>>> c = C()
並沒有傳入引數
>>> c = C(1, 2, 3)
傳入了 3 個引數,分別是:1 2 3

答:其實很容易啦,檢查下大家之前的知識點有沒有記牢固而已。

class C:
        def __init__(self, *args): #定義了一個可變引數
                if not args:
                        print("並沒有傳入引數")
                else:
                        print("傳入了 %d 個引數,分別是:" % len(args), end='')
                        for each in args:
                                print(each, end=' ')

1. 定義一個單詞(Word)類繼承自字串,重寫比較操作符(參考自學:Python 魔法方法詳解),當兩個 Word 類物件進行比較時,根據單詞的長度來進行比較大小。

加分要求:例項化時如果傳入的是帶空格的字串,則取第一個空格前的單詞作為引數。

答:加分要求可以通過過載 __new__ 方法來實現(因為字串是不可變型別),通過重寫 __gt__、__lt__、__ge__、__le__ 方法來定義 Word 類在比較操作中的表現。注意,我們沒有定義 __eq__ 和 __ne__ 方法。這是因為將會產生一些怪異不符合邏輯的結果(比如 Word('FishC') 會等於 Word('Apple'))

程式碼清單:

class Word(str):
'''儲存單詞的類,定義比較單詞的幾種方法'''

    def __new__(cls, word):
        # 注意我們必須要用到 __new__ 方法,因為 str 是不可變型別
        # 所以我們必須在建立的時候將它初始化
        if ' ' in word:
            print "Value contains spaces. Truncating to first space."
            word = word[:word.index(' ')] #單詞是第一個空格之前的所有字元
        return str.__new__(cls, word)

    def __gt__(self, other):
        return len(self) > len(other)
    def __lt__(self, other):
        return len(self) < len(other)
    def __ge__(self, other):
        return len(self) >= len(other)
    def __le__(self, other):
        return len(self) <= len(other)