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

Class
面向?qū)ο缶幊套钪匾母拍罹褪穷悾–lass)和實例(Instance),我們先來創(chuàng)建一個 Lxs 的類,它有兩個基本功 sing 和 dance ,lxs 是這個類的實例:
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í)時常1年半的老胡擅長唱和跳
恩,針不戳!
laohu practiced 1.5 years
laohu good at singing
laohu good at dancing
再來用__class__屬性或type()看看 Lxs 和 lxs 分別是誰創(chuàng)建的
print(lxs.__class__)
print(Lxs.__class__)
lxs 是 Lxs 的實例,它創(chuàng)建自 Lxs ,這很容易理解。
我們 Lxs 類是 type 創(chuàng)建的?
<class '__main__.Lxs'>
<class 'type'>
一切對象都來自 type
先說結(jié)論:type 可以動態(tài)創(chuàng)建 類(class) ,對象是類(class)的實例,類(class)也是對象,是 type 的實例。type 為對象的頂點,所有對象都創(chuàng)建自 type 。
當使用 type 創(chuàng)建 class 時,其用法如下:
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__這個屬性
此方法會在實例作為一個函數(shù)被“調(diào)用”時被調(diào)用
這里等號右邊的type(classname, superclasses, attributedict),就是 type 的__call__運算符重載,它會進一步調(diào)用:
type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)
# 這一部分我們以后有空再細品
總結(jié)一下:type 實際上是 Python 創(chuàng)建所有 class 的 metaclass。
metaclass
除了使用type()動態(tài)創(chuàng)建類以外,要控制類的創(chuàng)建行為,還可以使用metaclass。
先定義metaclass,就可以創(chuàng)建類,最后創(chuàng)建實例。
一句話:metaclass 是 type 的子類,是類的模板
metaclass 的主要目的是在 class 被創(chuàng)建的時候?qū)ι傻?class 進行自動的動態(tài)修改。
舉個例子:像老胡就只會 sing 和 dance,有人還會rap,有人會說相聲,我們定義很多的 class ,有一天,一個男人橫空出世,他會打籃球!然后,所有的練習(xí)生也都學(xué)會了籃球,這可怎么修改?
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 時,要通過LxsMetaclass.new()來創(chuàng)建,在此,我們可以修改類的定義,比如,加上新的方法basketball(),然后,返回修改后的定義。
我們用 LxsMetaclass 這個模板創(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()
運行結(jié)果如下,秀?
cxk practiced 2.5 years
cxk good at basketball
不過metaclass的作用肯定不限于此,舉個例子,也算是個思考題,大家品一品。
比如 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()
運行結(jié)果會是什么呢?
laohu practiced 0.5 years
laohu good at basketball
laohu 真的就只學(xué)會了籃球。。。
- EOF -
回復(fù)關(guān)鍵字“簡明python ”,立即獲取入門必備書籍《簡明python教程》電子版
回復(fù)關(guān)鍵字“爬蟲”,立即獲取爬蟲學(xué)習(xí)資料


