python类的应用_Python · 元类(Meta Class)及其应用
(這里是本章用到的 GitHub 地址)萬物皆對(duì)象 —— Python
本章所介紹的元類(Meta Class)和之前介紹過的裝飾器(Decorator)都是上面這句話的具現(xiàn),其中裝飾器告訴過我們“函數(shù)亦對(duì)象”,元類則會(huì)告訴我們“類亦對(duì)象”
Meta Class 是傳說中的黑魔法、黑魔法中的戰(zhàn)斗機(jī)(……)。我其實(shí)對(duì)它也只一知半解,所以以下說的內(nèi)容可能僅展現(xiàn)了它神奇功用的冰山一角。不過作為一個(gè)入門教程來說的話、可能會(huì)剛剛好也說不定(其實(shí)只是在為自己的弱小找借口)(喂)
所謂的“類亦對(duì)象”和“函數(shù)亦對(duì)象”的思想類似:它意味著類可以被賦值給變量、通過變量也能創(chuàng)建該類的實(shí)例。舉個(gè)栗子:
class Class:
def __init__(self):
self.x = 1
one = Class
print(one().x)
Out[1]:
1
正如裝飾器返回的是一個(gè)函數(shù),我們可以認(rèn)為元類返回的是一個(gè)類。 也正如我在講裝飾器里說過的,裝飾器的核心思想,就是裝飾函數(shù)這個(gè)對(duì)象、讓函數(shù)自身代碼不變的情況下、增添一些具有普適性的功能。在我看來,元類的核心思想,就是搗鼓類這個(gè)對(duì)象、使你能對(duì)其有著最高程度的控制權(quán)。
注意:這絕不一定是個(gè)準(zhǔn)確的理解!正如 Python界的領(lǐng)袖 Tim Peters 說過:元類就是深度的魔法,99%的用戶應(yīng)該根本不必為此操心。如果你想搞清楚究竟是否需要用到元類,那么你就不需要它。那些實(shí)際用到元類的人都非常清楚地知道他們需要做什么,而且根本不需要解釋為什么要用元類。
我的理解僅僅來自于我對(duì)元類的應(yīng)用,它很有可能是非常片面的。不過,由于我的目的是為了讓大家知道元類的一種可能是最簡單的使用姿勢(shì)、使大家不至于看到代碼里面的 metaclass 就怕,所以還請(qǐng)觀眾老爺們?cè)试S我這個(gè)半吊子繼續(xù)用這個(gè)理解講下去(如果有觀眾老爺有更深更好的理解、歡迎在評(píng)論區(qū)里面教我、我會(huì)把它們貼在這里的 ( σ'ω')σ)
那么什么叫做最高程度的控制權(quán)呢?一個(gè)比較簡單的栗子就是實(shí)現(xiàn)如下需求:定義一個(gè)“人”(Person)類,它有三個(gè)方法:吃飯、睡覺、續(xù)幾秒(咦)
定義 Person 的三個(gè)子類“小張”(Zhang)、“小王”(Wang)、“小江”(Jiang)
定義“人”的子類“小紅”(Hong), 要求他:吃飯像小張一樣快
睡覺像小王一樣香
續(xù)秒像小江一樣熟練(喂)
你會(huì)怎么去實(shí)現(xiàn)呢?如果再要求你把上面三個(gè)要求換一換順序呢?
也許 Python 有許多其它的解決方案、但(我所知道的)最簡單的方法、就是使用元類了
幸運(yùn)的是,雖然元類的思想可能很深,但就這個(gè)簡單的問題而言、即使我不進(jìn)行任何說明、相信聰明的觀眾老爺們也能讀懂下面這幾塊代碼
先定義 Person 類:
class Person:
def __init__(self):
self.ability = 1
def eat(self):
print("Eat: ", self.ability)
def sleep(self):
print("Sleep: ", self.ability)
def save_life(self):
print("+ ", self.ability, " s")
再定義三個(gè)子類:
class Wang(Person):
def eat(self):
print("Eat: ", self.ability * 2)
class Zhang(Person):
def sleep(self):
print("Sleep: ", self.ability * 2)
class Jiang(Person):
def save_life(self):
print("+ inf s")
然后是最關(guān)鍵的、定義元類(Meta Class):
class Mixture(type):
def __new__(mcs, *args, **kwargs):
name, bases, attr = args[:3]
person1, person2, person3 = bases
def eat(self):
person1.eat(self)
def sleep(self):
person2.sleep(self)
def save_life(self):
person3.save_life(self)
attr["eat"] = eat
attr["sleep"] = sleep
attr["save_life"] = save_life
return type(name, bases, attr)
Done!可能會(huì)有觀眾老爺發(fā)現(xiàn)其中有三行代碼顯得“特別傻”——沒錯(cuò),確實(shí)可以用更具有普適性的三行代碼來代替我們上面倒數(shù)第二到第四行的代碼:
class Mixture(type):
def __new__(mcs, *args, **kwargs):
name, bases, attr = args[:3]
person1, person2, person3 = bases
def eat(self):
person1.eat(self)
def sleep(self):
person2.sleep(self)
def save_life(self):
person3.save_life(self)
for key, value in locals().items():
if str(value).find("function") >= 0:
attr[key] = value
return type(name, bases, attr)
拋開所有技術(shù)細(xì)節(jié)而只談應(yīng)用的話、其實(shí)上面這個(gè)栗子可能已經(jīng)相當(dāng)足夠了。接下來就讓我們測(cè)試一下這個(gè) Mixture元類吧。先來定義一個(gè)小的測(cè)試函數(shù),它依次調(diào)用 Person 實(shí)例吃飯、睡覺、續(xù)幾秒這三個(gè)動(dòng)作:
def test(person):
person.eat()
person.sleep()
person.save_life()
然后進(jìn)行兩組測(cè)試:
class Hong(Wang, Zhang, Jiang, metaclass=Mixture):
pass
test(Hong())
Out[2]:
Eat: 2
Sleep: 2
+ inf s
class Hong(Zhang, Wang, Jiang, metaclass=Mixture):
pass
test(Hong())
Out[3]:
Eat: 1
Sleep: 1
+ inf s
Done!可以看到、我們確實(shí)獲得了類的高度控制權(quán)
可能會(huì)有觀眾老爺想問,如果我直接繼承會(huì)發(fā)生什么事情?就這個(gè)栗子而言,如果定義一個(gè)類直接繼承小王、小張、小江的話,無論按什么順序繼承、結(jié)果都會(huì)是一樣的(猜猜這個(gè)結(jié)果會(huì)是什么? ( σ'ω')σ)
值得一提的是,可以看到我們定義的元類繼承自 type、這是因?yàn)?Python 自帶的元類就是 type
其實(shí)即使僅僅基于上述栗子的思想、就已經(jīng)可以搗鼓出許多有意思的應(yīng)用了。在我自己實(shí)現(xiàn)的神經(jīng)網(wǎng)絡(luò)中、我就用了這個(gè)思想來把普通 NN 里面的附加層(Dropout、Normalize)擴(kuò)展成了 CNN 里面的附加層,感興趣的觀眾老爺們可以看看這里面的 ConvSubMeta 類
我沒有講太多原理層面的東西,一方面是因?yàn)槲矣X得知道怎么用就好、另一方面是因?yàn)槲遗聛y說話遭報(bào)應(yīng)(……)
希望觀眾老爺們能夠喜歡~
總結(jié)
以上是生活随笔為你收集整理的python类的应用_Python · 元类(Meta Class)及其应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle 连续5分钟_3-1!10分
- 下一篇: python重复字符串n次_python