動態附加物件方法
在定義一個class之後, 除了可以在類別內定義物件變數, 也可以在外部臨時動態附加其他的物件變數, 這是動態語言的靈活性.
class Pokemon(object): pass p1=Pokemon() p1.name="皮卡丘" # 動態附加新的物件變數 print("%s" % p1.name) p2=Pokemon() print("p2沒有物件變數 : name") 結果: 皮卡丘 p2沒有物件變數 : name
如果物件變數可以動態附加, 那物件方法是否也可以動態附加呢, 答案是肯定的
首先定義一個方法, 此方法第一個參數一定要是self. 在動態方法中, 甚至可以動態附加物件變數. 最後使用MethodType(方法名, 物件名)取得方法後, 放入物件中. 請注意, MethodType需先import
from types import MethodType class Pokemon(object): pass p1=Pokemon() def setLevel(self, l):#定義方法, 因為是物件方法, 所以要加self self.level=l#甚至可以在動態方法中附加動態物件變數 p1.setLevel=MethodType(setLevel, p1)#使用MethodType取得方法再附加到p1上 p1.setLevel(100) print(p1.level)
p1動態附加方法後, 對於其他實例是起不了任何作用的. 如果希望其他實例也可調到此方法的話, 那就要把動態方法附加在class裏
Pokemon.setLevel=setLevel
強型語言只能靜態的寫在class中, 而Python這種動態附加給class的功能, 可以讓我們在程式碼運行時才決定要不要附加, 這是靜態語言無法實現的.
from types import MethodType
class Pokemon(object):
pass
p1=Pokemon()
def setLevel(self, l):#定義方法, 因為是物件方法, 所以要加self
self.level=l#甚至可以在動態方法中附加動態物件變數
p1.setLevel=MethodType(setLevel, p1)#使用MethodType取得方法再附加到p1上
p1.setLevel(100)
print(p1.level)
Pokemon.setLevel=setLevel
p2=Pokemon()
p2.setLevel(5000)
print(p2.level)
而且Python神奇的是, 在class動態附加方法之前就產生出來的實例, 一樣具有此新功能. 更厲害的是, 連帶子類別的實例都能擁有此新功能. 站在程式設計師的立場, 這機制要絕頂聰明的人才設計的出來.
class Pokemon(object): ......定義class p1=Pokemon() ......產生實例 Pokemon.setLevel=setLevel ......class附加新功能 p1.setLevel() ......p1也會有setLevel新功能
白名單 slots
這種動態附加新功能的機制, 就好比人類出生後, 經過努力學習而得到了新的技術. 話是說的很正能量啦, 人也是會在出生後, 經過學習而得到了吃喝嫖賭的技術.
一個好好的class, 因為交了壞朋友, 那會不會變成病毒啊. 這個叫廢話, 而且是很容易滴
為了不讓class任意的長大, 只好限制它的成長. __slots__就是為此目的而生的. slots就好比是一個白名單, 只有在名單內的值, 才充許被增加
class Pokemon(object): __slots__=('setLevel', 'w', 'h')
在slots裏的值, 是以tuple的格式定義. 可以是方法名, 也可以是變數名.
如果附加的是方法, 那麼在方法內附加的物件變數, 也要加入slots, 否則也會被限制住.
請注意, slots只會限制類別本身, 子類別不受影響. 但如果子類別也寫 slots的話, 那白名單的內容會包含父類別的白名單