元類

元類允許我們攔截并擴(kuò)展類創(chuàng)建----提供了一個(gè)API以插入在一條class語句結(jié)束時(shí)運(yùn)行的二維邏輯,盡管是以與裝飾器不同的方式静暂。

類是類型的實(shí)例

  • 在Python3.0中济丘,用戶定義的類對象是名為type的對象的實(shí)例,type本身是一個(gè)類
  • 類型由派生自type的類定義
  • 用戶定義的類是類型類的實(shí)例
  • 用戶定義的類產(chǎn)生他們自己的實(shí)例的類型

1洽蛀、元類是Type的子類
2摹迷、type是產(chǎn)生用戶定義的類的一個(gè)類
3、類對象是type類的一個(gè)實(shí)例郊供,或一個(gè)子類
4泪掀、實(shí)例對象產(chǎn)生自一個(gè)類

Class語句協(xié)議
當(dāng)Python遇到一條class語句,它會(huì)運(yùn)行其嵌套的代碼塊以創(chuàng)建其屬性----所有在嵌套代碼塊的頂層分配的名稱都產(chǎn)生結(jié)果類對象中的屬性颂碘。在一條class語句的末尾,并且在運(yùn)行了一個(gè)命名空間詞典中的所有嵌套代碼之后椅挣,它調(diào)用type對象來創(chuàng)建class對象

class = type(classname,superclass,attrbutedict)

type對象反過來定義一個(gè)call運(yùn)算符重載方法头岔,當(dāng)調(diào)用type對象的時(shí)候,該方法運(yùn)行兩個(gè)其他的方法:

type.new(typeclass,classname,superclasses,attributedict)
type.init(class,classname,superclasses,attributedict)
new方法創(chuàng)建并返回一個(gè)新的class對象鼠证,并且隨后init方法初始化了新創(chuàng)建的對象

例如

class Spam(Eggs):
    data = 1
    def meth(self,arg):
        pass
Python將會(huì)從內(nèi)部運(yùn)行嵌套的代碼來創(chuàng)建該類的兩個(gè)屬性(data和meth)峡竣,然后在class語句的末尾調(diào)用type對象,產(chǎn)生class對象:
spam = type('Spam',(Eggs,),{'data':1,'meth':meth,'__module__':'__main__'})

聲明元類

class Spam(metaclass=Meta):
    pass
當(dāng)以這種方式聲明的時(shí)候量九,創(chuàng)建類對象的調(diào)用在class語句的底部運(yùn)行适掰,修改為調(diào)用元類而不是默認(rèn)的type:
class = Meta(classname,superclass,attributedict)
由于元類是type的一個(gè)子類颂碧,所以type類的__call__把創(chuàng)建和初始化新的類對象的調(diào)用委托給元類
Meta.__new__(Meta,classname,superclass,attributedict)
Meta.__init__(class,classname,superclass,attributedict)

------------------------------
class Spam(Eggs,metaclass = Meta):
    data = 1
    def meth(self,arg):
        pass
"""
這條class語句的末尾,Python內(nèi)部運(yùn)行如下的代碼來創(chuàng)建class對象
Spam = Meta('Spam',(Eggs,),{'data':1,'meth':meth,'__module__':'__main__'})
"""

基本元類

#-*-coding:UTF-8-*-
class MetaOne(type):
    def __new__(meta,classname,supers,classdict):
        print('In MetaOne.new',classname,supers,classdict,sep='n..')
        return type.__new__(meta,classname,supers,classdict)
    def __init__(Class,classname,supers,classdict):
        print('In MetaOne init',classname,supers,classdict,sep='n...')
        print(list(Class.__dict__.keys()))
class Eggs:
    pass
print('making class')
class Spam(Eggs,metaclass = MetaOne):
    data = 1
    def meth(self,arg):
        pass
if __name__ == '__main__':
    print('making instance')
    X = Spam()
    print('data:',X.data)
>>>
>>>
making class
In MetaOne.new  #元類處理類
..Spam
..(<class '__main__.Eggs'>,)
..{'__qualname__': 'Spam', 'meth': <function Spam.meth at 0x0060A150>, '__module__': '__main__', 'data': 1}
In MetaOne init
...Spam
...(<class '__main__.Eggs'>,)
...{'__qualname__': 'Spam', 'meth': <function Spam.meth at 0x0060A150>, '__module__': '__main__', 'data': 1}
['meth', '__module__', '__doc__', 'data']
making instance  #類用來處理實(shí)例
data: 1
###
Spam繼承自Eggs并且是MetaOne的一個(gè)實(shí)例类浪,在我們真正創(chuàng)建一個(gè)實(shí)例之前---元類用來處理類载城,并且類用來處理實(shí)例。通常__new__創(chuàng)建并返回了類對象费就,__init__初始化了已經(jīng)創(chuàng)建了類诉瓦。

其他元類編程技巧
1>使用簡單的工廠函數(shù)
實(shí)際上任何可調(diào)用對象都可以用作一個(gè)元類,只要它接收傳遞的參數(shù)并且返回與目標(biāo)兼容的一個(gè)對象力细。

def MetaFunc(classname,supers,classdict):
    print('In MetaFunc: ',classname,supers,classdict,sep='n...')
    return type(classname,supers,classdict) 
class Eggs:
    pass
print('making class')
class Spam(Eggs,metaclass = MetaFunc):
    data = 1
    def meth(self,arg):
        pass
if __name__ == '__main__':
    print('making instance')
    X = Spam()
    print('data:',X.data)
###
當(dāng)在聲明Spam類時(shí)睬澡,在class語句的末尾,即
metaclass = MetaFunc
即調(diào)用
class = MetaFunc(classname,supers,classdict)
傳遞
MetaFunc('Spam',(Eggs,),{'data':1,'meth':meth,'__module__':'__main__'})

2>用元類重載類創(chuàng)建調(diào)用

#-*-coding:UTF-8-*-
"""
一個(gè)元類的元類
"""
class SuperMeta(type):
    def __call__(meta,classname,supers,classdict):
        print('In SuperMeta.call:',classname,supers,classdict,sep='n..')
        return type.__call__(meta,classname,supers,classdict)

class SubMeta(type,metaclass=SuperMeta):
    def __new__(meta,classname,supers,classdict):
        print('In SubMeta.new: ',classname,supers,classdict,sep='n...')
        return type.__new__(meta,classname,supers,classdict)
    def __init__(Class,classname,supers,classdict):
        print('In SubMeta init: ',classname,supers,classdict,sep='n...')
        print('...init class object: ',list(Class.__dict__.keys()))

class Eggs:
    pass
print('making class')

class Spam(Eggs,metaclass=SubMeta):
    data = 1
    def meth(self,arg):
        pass

if __name__ == '__main__':
    print('Makig instance')
    X = Spam()
    print('data: ',X.data)
>>>
making class
In SuperMeta.call:
..Spam
..(<class '__main__.Eggs'>,)
..{'__qualname__': 'Spam', '__module__': '__main__', 'data': 1, 'meth': <function Spam.meth at 0x0061A150>}
In SubMeta.new: 
...Spam
...(<class '__main__.Eggs'>,)
...{'__qualname__': 'Spam', '__module__': '__main__', 'data': 1, 'meth': <function Spam.meth at 0x0061A150>}
In SubMeta init: 
...Spam
...(<class '__main__.Eggs'>,)
...{'__qualname__': 'Spam', '__module__': '__main__', 'data': 1, 'meth': <function Spam.meth at 0x0061A150>}
...init class object:  ['__doc__', '__module__', 'data', 'meth']
Makig instance
data:  1
###
把上文中的__call__函數(shù)放在SubMeta中是不行的眠蚂。
同時(shí)__new__是一定要存在的

3>用常規(guī)類重載類創(chuàng)建調(diào)用

class SuperMeta:
    def __call__(self,classname,supers,classdict):
        print('In SuperMeta.call: ',classname,supers,classdict,sep='n...')
        Class = self.__New__(classname,supers,classdict)
        self.__Init__(Class,classname,supers,classdict)
        return Class

class SubMeta(SuperMeta):
    def __New__(self,classname,supers,classdict):
        return type(classname,supers,classdict)

    def __Init__(self,Class,classname,supers,classdict):
        print('In SubMeta.new',classname,supers,classdict)
        print('..init class object: ',list(Class.__dict__.keys()))

class Eggs:pass

class Spam(Eggs,metaclass=SubMeta()):
    data = 1
    def meth(self,arg):
        pass

print('making instance')
if __name__ == '__main__':
    x = Spam()
    print(x.data)

注意2和3的不同
實(shí)例與繼承的關(guān)系
<li>元類繼承自type類煞聪。元類是用class語句編寫的,并遵從OOP模型逝慧。元類通常重新定義type類的newinit昔脯。以定制類創(chuàng)建和初始化,但是馋艺,如果他們希望直接捕獲類末尾的創(chuàng)建調(diào)用的話栅干,也可以重新定義call
<li>元類聲明由子類繼承。在用戶定義的類中捐祠,metaclass=M聲明由該類的子類繼承碱鳞,因此,對于在超類鏈中繼承了這一聲明的每個(gè)類的構(gòu)建踱蛀,該元類都將運(yùn)行窿给。
<li>元類屬性沒有由子類實(shí)例繼承。元類聲明指定了一個(gè)實(shí)例關(guān)系率拒,和繼承不同崩泡。由于類是元類的實(shí)例,所以元類中定義的行為應(yīng)用于類猬膨,而不是類隨后的實(shí)例角撞。實(shí)例從他們的類和超類中獲取行為,而不是從元類中勃痴。實(shí)例屬性查找通常搜索實(shí)例及其所有類的dict字典


向類中添加方法

def eggsfunc(obj):
    return obj.value * 4
def hamfunc(obj,value):
    return value+' ham'
class Extender(type):
    """
    添加額外的方法谒所。這里可以添加條件判斷執(zhí)行不同的方法
    """
    def __new__(meta,classname,supers,classdict):
        classdict['eggs'] = eggsfunc
        classdict['ham'] = hamfunc
        return type.__new__(meta,classname,supers,classdict)

class Client1(metaclass=Extender):
    def __init__(slef,value):
        slef.value = value
    def spam(slef):
        return slef.value * 2
if __name__ == '__main__':
    x = Client1('Ni!')
    print(x.spam())
    print(x.eggs())
###
繼承也可以為類添加相同的兩個(gè)方法。然而元類結(jié)構(gòu)支持更多的動(dòng)態(tài)行為沛申×恿欤可以在__new__方法中執(zhí)行判斷,執(zhí)行不同的方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铁材,一起剝皮案震驚了整個(gè)濱河市尖淘,隨后出現(xiàn)的幾起案子奕锌,更是在濱河造成了極大的恐慌,老刑警劉巖村生,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惊暴,死亡現(xiàn)場離奇詭異,居然都是意外死亡梆造,警方通過查閱死者的電腦和手機(jī)缴守,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镇辉,“玉大人屡穗,你說我怎么就攤上這事『龈兀” “怎么了村砂?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長屹逛。 經(jīng)常有香客問我础废,道長,這世上最難降的妖魔是什么罕模? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任评腺,我火速辦了婚禮,結(jié)果婚禮上淑掌,老公的妹妹穿的比我還像新娘蒿讥。我一直安慰自己,他們只是感情好抛腕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布芋绸。 她就那樣靜靜地躺著,像睡著了一般担敌。 火紅的嫁衣襯著肌膚如雪摔敛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天全封,我揣著相機(jī)與錄音马昙,去河邊找鬼。 笑死刹悴,一個(gè)胖子當(dāng)著我的面吹牛给猾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颂跨,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扯饶!你這毒婦竟也來了恒削?” 一聲冷哼從身側(cè)響起池颈,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钓丰,沒想到半個(gè)月后躯砰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡携丁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年琢歇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梦鉴。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡李茫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肥橙,到底是詐尸還是另有隱情魄宏,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布存筏,位于F島的核電站宠互,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏椭坚。R本人自食惡果不足惜予跌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望善茎。 院中可真熱鬧券册,春花似錦、人聲如沸巾表。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽集币。三九已至考阱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鞠苟,已是汗流浹背乞榨。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留当娱,地道東北人吃既。 一個(gè)月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像跨细,于是被迫代替她去往敵國和親鹦倚。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 元類與裝飾器的關(guān)系 在class語句的末尾冀惭,類裝飾器把類名重新綁定到一個(gè)函數(shù)的結(jié)果震叙。 元類通過一條class語句的...
    低吟淺唱1990閱讀 394評論 0 0
  • 寫在前面 這兩天仔細(xì)研究了python中元類的概念掀鹅,從最開始的一頭霧水,到現(xiàn)在的漸漸有一點(diǎn)明白媒楼。想借這篇文章來闡述...
    光的文明閱讀 450評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理乐尊,服務(wù)發(fā)現(xiàn),斷路器划址,智...
    卡卡羅2017閱讀 134,665評論 18 139
  • 一元類 1類也是對象 在大多數(shù)編程語言中夺颤,類就是一組用來描述如何生成一個(gè)對象的代碼段痢缎。在Python中這一點(diǎn)仍然成...
    五行缺覺閱讀 1,061評論 0 1
  • 元類被稱為 Python 中的“深?yuàn)W的巫術(shù)”。盡管你需要用到它的地方極少(除非你基于 zope編程)拂共,可事實(shí)上它的...
    chen_000閱讀 209評論 0 0