元類允許我們攔截并擴(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類的new和init昔脯。以定制類創(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í)行不同的方法