Python 中最黑魔法、最難懂的概念
大家好,我是老胡
最近在看一個(gè)開(kāi)源框架的源碼,其中大量使用了 metaclass 方法。這個(gè)概念非常抽象,本文我就以一個(gè)有趣實(shí)例,用更簡(jiǎn)潔和通暢的方式來(lái)理解它。
元類 ( metaclass )應(yīng)該是 Python 中最黑魔法、最難懂的概念之一,它提供了創(chuàng)造新類型的能力,為程序設(shè)計(jì)帶來(lái)更多可能性。不少功能強(qiáng)大的開(kāi)發(fā)框架,內(nèi)部實(shí)現(xiàn)離不開(kāi) metaclass 的魔法。

Class
面向?qū)ο缶幊套钪匾母拍罹褪穷悾–lass)和實(shí)例(Instance),我們先來(lái)創(chuàng)建一個(gè) Lxs 的類,它有兩個(gè)基本功 sing 和 dance ,lxs 是這個(gè)類的實(shí)例:
class Lxs(object):
def __init__(self, name, duration):
self.name = name
self.duration = duration
print('%s practiced %s years' % (self.name, self.duration))
def sing(self):
print('%s good at singing' % self.name)
def dance(self):
print('%s good at dancing' % self.name)
lxs = Lxs('laohu',1.5)
lxs.sing()
lxs.dance()
練習(xí)時(shí)常1年半的老胡擅長(zhǎng)唱和跳
恩,針不戳!
laohu practiced 1.5 years
laohu good at singing
laohu good at dancing
再來(lái)用__class__屬性或type()看看 Lxs 和 lxs 分別是誰(shuí)創(chuàng)建的
print(lxs.__class__)
print(Lxs.__class__)
lxs 是 Lxs 的實(shí)例,它創(chuàng)建自 Lxs ,這很容易理解。
我們 Lxs 類是 type 創(chuàng)建的?
<class '__main__.Lxs'>
<class 'type'>
一切對(duì)象都來(lái)自 type
先說(shuō)結(jié)論:type 可以動(dòng)態(tài)創(chuàng)建 類(class) ,對(duì)象是類(class)的實(shí)例,類(class)也是對(duì)象,是 type 的實(shí)例。type 為對(duì)象的頂點(diǎn),所有對(duì)象都創(chuàng)建自 type 。
當(dāng)使用 type 創(chuàng)建 class 時(shí),其用法如下:
class = type(classname, superclasses, attributedict)
'''
classname:類名
superclasses:類的繼承關(guān)系,用元組表示
attributedict:表示各種屬性、方法,用字典表示
'''
繼續(xù)上例,先定義__init__,sing 和 dance ,然后用 type 可以創(chuàng)建和上面完全一樣的類:
Lxs = type('Lxs', (object,), dict( __init__= __init__,sing=sing,dance=dance))
lxs = Lxs('laohu',1)
lxs.sing()
print(lxs.__class__)
print(Lxs.__class__)
這里不得不提一下__call__這個(gè)屬性
此方法會(huì)在實(shí)例作為一個(gè)函數(shù)被“調(diào)用”時(shí)被調(diào)用
這里等號(hào)右邊的type(classname, superclasses, attributedict),就是 type 的__call__運(yùn)算符重載,它會(huì)進(jìn)一步調(diào)用:
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)
# 這一部分我們以后有空再細(xì)品
總結(jié)一下:type 實(shí)際上是 Python 創(chuàng)建所有 class 的 metaclass。
metaclass
除了使用type()動(dòng)態(tài)創(chuàng)建類以外,要控制類的創(chuàng)建行為,還可以使用metaclass。
先定義metaclass,就可以創(chuàng)建類,最后創(chuàng)建實(shí)例。
一句話:metaclass 是 type 的子類,是類的模板
metaclass 的主要目的是在 class 被創(chuàng)建的時(shí)候?qū)ι傻?class 進(jìn)行自動(dòng)的動(dòng)態(tài)修改。
舉個(gè)例子:像老胡就只會(huì) sing 和 dance,有人還會(huì)rap,有人會(huì)說(shuō)相聲,我們定義很多的 class ,有一天,一個(gè)男人橫空出世,他會(huì)打籃球!然后,所有的練習(xí)生也都學(xué)會(huì)了籃球,這可怎么修改?
metaclass 就可以施展魔法了
class LxsMetaclass(type):
def __new__(cls, cls_name, bases, attrs):
def basketball(self):
print('%s good at basketball' % self.name)
attrs['basketball'] = basketball
return super(LxsMetaclass, cls).__new__(cls, cls_name, bases, attrs)
它指示Python解釋器在創(chuàng)建 LxsMetaclass 時(shí),要通過(guò)LxsMetaclass.new()來(lái)創(chuàng)建,在此,我們可以修改類的定義,比如,加上新的方法basketball(),然后,返回修改后的定義。
我們用 LxsMetaclass 這個(gè)模板創(chuàng)建類:
class Cxk(object, metaclass=LxsMetaclass):
def __init__(self, name, duration):
self.name = name
self.duration = duration
print('%s practiced %s years' % (self.name, self.duration))
def sing(self):
print('%s good at singing' % self.name)
def dance(self):
print('%s good at dancing' % self.name)
def rap(self):
print('%s good at rap' % self.name)
cxk = Cxk('cxk',2.5)
cxk.basketball()
運(yùn)行結(jié)果如下,秀?
cxk practiced 2.5 years
cxk good at basketball
不過(guò)metaclass的作用肯定不限于此,舉個(gè)例子,也算是個(gè)思考題,大家品一品。
比如 laohu 化身 xck 的粉絲,打著學(xué)籃球的幌子學(xué) sing、dance和rap :
class Funs(Cxk):
def basketball(self):
print('%s good at singing&dancing&rap' % self.name)
fans = Funs('laohu',0.5)
fans.basketball()
運(yùn)行結(jié)果會(huì)是什么呢?
laohu practiced 0.5 years
laohu good at basketball
laohu 真的就只學(xué)會(huì)了籃球。。。
這是為何呢?且聽(tīng)下回分解。
兄弟們,來(lái)個(gè)三連可好?轉(zhuǎn)發(fā),在看,點(diǎn)贊
··· END ··· 推薦閱讀: 一、Number(數(shù)字) Python基礎(chǔ)之?dāng)?shù)字(Number)超級(jí)詳解 Python隨機(jī)模塊22個(gè)函數(shù)詳解 Python數(shù)學(xué)math模塊55個(gè)函數(shù)詳解 二、String(字符串) Python字符串的45個(gè)方法詳解 Pandas向量化字符串操作 三、List(列表) 超級(jí)詳解系列-Python列表全面解析 Python輕量級(jí)循環(huán)-列表推導(dǎo)式 四、Tuple(元組) Python的元組,沒(méi)想象的那么簡(jiǎn)單 五、Set(集合) 全面理解Python集合,17個(gè)方法全解,看完就夠了 六、Dictionary(字典) Python字典詳解-超級(jí)完整版 七、內(nèi)置函數(shù) Python初學(xué)者必須吃透這69個(gè)內(nèi)置函數(shù)! 八、正則模塊 Python正則表達(dá)式入門到入魔 筆記 | 史上最全的正則表達(dá)式 八、系統(tǒng)操作 Python之shutil模塊11個(gè)常用函數(shù)詳解 Python之OS模塊39個(gè)常用函數(shù)詳解 九、進(jìn)階模塊 【萬(wàn)字長(zhǎng)文詳解】Python庫(kù)collections,讓你擊敗99%的Pythoner 高手如何在Python中使用collections模塊 ↓掃描關(guān)注本號(hào)↓

