12.python-metaclass元類
1.python中一切皆是對象,類本身也是一個對象,當使用關鍵字class的時候,python解釋器在加載class的時候會創建一個對象(這裏的對象指的是類而非類的實例)
class Foo: pass #f1是通過Foo類實例化的對象 f1=Foo() #通過type查看f1這個對象由哪一個類產生 print(type(f1)) #輸出:<class ‘__main__.Foo‘> f1這個對象由Foo類創建 #通過type查看Foo這個類由哪一個對象產生 print(type(Foo)) #輸出:<class ‘type‘> Foo這個類的類是type,type是python的一個內建元類
返回:
<class ‘__main__.Foo‘>
<class ‘type‘>
2.什麽是元類
(1)元類是類的類,是類的模版
(2)元類是用來控制如何創建類的,正如類是創建對象的模版一樣
(3)元類的實例為類,正如類的實例為對象(f1對象是Foo類的一個實例,Foo類是type類的一個實例)
(4)typy是python的一個內建元類(不指定類的類是誰默認用typy去生成),用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象
3.創建元類的兩種方式
方式一:
class Foo: def __init__(self): #加上構造函數__init__pass #查看Foo這個類 print(Foo) #查看Foo這個類裏的屬性 print(Foo.__dict__)
返回:
class ‘__main__.Foo‘>
{‘__module__‘: ‘__main__‘, ‘__init__‘: <function Foo.__init__ at 0x00399858>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Foo‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Foo‘ objects>, ‘__doc__‘: None}
方式二:
def __init__(self,name,age): #頂級作用域裏定義構造函數__init__ self.name=name self.age=age def test(self): #創造test方法 print(‘我是test方法‘) #創建元類FFo是由type類產生的 FFo=type(‘FFo‘,(object,),{‘x‘:1,‘__init__‘:__init__,‘test‘:test}) #實例化type傳三個參數進去,參數1:類名字符串形式,參數2:繼承的父類元祖的形式(新式類默認object),參數3:類的屬性‘x‘:1和方法屬性__init__‘:__init__和test‘:test放在屬性字典裏 #查看Foo這個類 print(FFo) #用FFo這個對象產生實例傳倆個參數xixi和18 f1=FFo(‘xixi‘,18) #調f1的x的類屬性 print(f1.x) #返回:1 #調f1的name的類屬性 print(f1.name) #返回:xixi #調f1的方法實例化 f1.test() #返回:我是test方法 #查看Foo這個類裏的屬性 print(FFo.__dict__)
返回:
<class ‘__main__.FFo‘>
1
xixi
我是test方法
{‘x‘: 1, ‘__init__‘: <function __init__ at 0x01D8C6A8>, ‘test‘: <function test at 0x02109858>, ‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘FFo‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘FFo‘ objects>, ‘__doc__‘: None}
4.一個類沒有聲明自己的元類,默認他的元類就是type,除了使用元類type,用戶也可以通過繼承type來自定義元類
5.元類如何控制類的創建
#定制元類必MyType須繼承type類 class MyType(type): def __init__(self,a,b,c): #第二步:接收metaclass=MyType傳來的四個參數,得到結果Foo,Foo這個類就此生成 print(‘觸發元類的構造函數執行‘) #print(a) #參數二傳過來的類名 #print(b) #參數三傳過來繼承的object #print(c) #參數四傳過來類的屬性字典 def __call__(self, *args, **kwargs): #第四步:接收self是Foo本身,*args, **kwargs接收Foo傳的參數xixi print(‘觸發元類裏__call__方法‘) #print(self) #查看self是Foo:<class ‘__main__.Foo‘> #print(args,kwargs) #查看*args, **kwargs傳過來的值(‘xixi‘,) {} obj = object.__new__(self) #產生一個對象 #第五步:生成Foo的對象。所有類都繼承object,object.__new__就是創建一個新的對象把self傳進去,self就是Foo,得到的就是Foo這個對象,Foo產生的對象就是f1賦值給obj self.__init__(obj, *args, **kwargs) #第六步:給f1做封裝的屬性的事。self.__init__相當於執行Foo.__init__ 就是在執行def __init__(self,name):要傳倆個參數self傳obj就是f1 name傳 *args, **kwargs return obj #第七步:返回obj。__call__得到返回值才賦值給f1 #定義Foo這個類聲明它的元類是MyType,只要一class Foo會觸發MyType(‘Foo‘,(object,),{}) class Foo(metaclass=MyType): #第一步:執行class Foo會觸發class MyType(type):傳四個參數給__init__方法 def __init__(self,name): #第八步:跳到Foo的__init__方法 self.name=name #第九步:給f1封裝屬性,name的屬性封裝到f1的屬性字典裏。得到f1.name=name #調用 f1=Foo(‘xixi‘) #第三步:Foo(‘xixi‘) 在呼叫MyType(type):裏的__call__方法 #第十步:__call__運行完畢代表Foo(‘xixi‘) 運行完畢把變量返值回給f1 #查看f1 print(f1) #查看f1字典裏的屬性 print(f1.__dict__)
返回:
觸發元類的構造函數執行
觸發元類裏__call__方法
<__main__.Foo object at 0x027C49D0>
{‘name‘: ‘xixi‘}
12.python-metaclass元類