第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學習:反運算 來自:南淮北安,連結:
>>> 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)