印象中,是在建立單例模式時知道能夠用到元類(metaclass),但始終對其瞭解的不是很透徹,不少人也都說元類是Python中較難理解的概念之一,因而找來幾本書,但願能夠找到答案,本文以Python3爲例。python
本文參考:設計模式
《人人都懂設計模式》函數
《Python Cookbook》設計
《 流暢的Python》code
先來簡單介紹下:元類(metaclass)是一個類,你也能夠理解爲類的類,由於Python中的類是在運行時動態建立的,那麼經過元類即可以控制類屬性和類實例的建立過程。對象
來看看用元類實現的單例模式:blog
class Singleton(type): """ 單例模式 """ def __init__(cls, *args, **kwargs): cls.__instance = None super().__init__(*args, **kwargs) def __call__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super().__call__(*args, **kwargs) return cls.__instance class Test(metaclass=Singleton): def __init__(self): pass a = Test() b = Test() print(id(a)) print(id(b))
具體的實現是:建立類時顯式的指定類的metaclass,而自定義的metaclass繼承type,並從新實現__call__
方法。繼承
因而,有了兩個問題:源碼
由於,在Python中,type是默認的metaclass(內建元類),Python容許咱們自定義metaclass,自定義的metaclass必須繼承自type,也就是:元類從type類繼承了構建類的能力。it
咱們一般用type來獲取對象所屬的類,就像這樣:
In [10]: a = 10 In [11]: type(a) Out[11]: int
然而,type仍是一個類,你能夠經過type來新建一個類,看type的源碼,經過type(name, bases, dict)
即可以生成一個新的類:
In [44]: test_class = type('Test', (), dict({'name': None})) In [45]: a = test_class() In [46]: a.name = 'Tony' In [47]: a.name Out[47]: 'Tony'
默認狀況下,Python中類都是type類的實例:
In [12]: class A: ...: pass ...: In [13]: A.__class__ Out[13]: type In [14]: int.__class__ Out[14]: type
當你使用class關鍵字時,Python在幕後作的事情,就是經過元類來實現的。
__call__
方法?提出該問題是由於,與Python類建立相關的方法是:
__new__
:類方法,負責對象的建立,在定義類時須要返回一個實例,在咱們經過類名進行實例化對象時自動調用。
__init__
:初始化函數,負責對new實例化的對象進行初始化,負責對象狀態的更新和屬性的設置,在每一次實例化對象以後調用。
而咱們經常使用__call__
方法只是爲了聲明這個類的對象是可調用的(callable)。
可是,在metaclass中__call__
方法還負責對象的建立,這就是爲何要從新定義的緣由了。
重定義了__call__
方法以後,一個對象的建立過程大概以下圖:
咱們驗證一下:
class TestMetaClass(type): def __init__(cls, what, bases=None, dict=None): print("metaclass init") super().__init__(what, bases, dict) def __call__(cls, *args, **kwargs): print("metaclass call") self = super(TestMetaClass, cls).__call__(*args, **kwargs) return self class TestClass(metaclass=TestMetaClass): def __init__(self, *args, **kwargs): print("class init") super().__init__() def __new__(cls, *args, **kwargs): print("class new") self = super().__new__(cls) return self a = TestClass()
返回:
metaclass init metaclass call class new class init
能夠看到,__call__
方法在類執行__new__
和__init__
以前執行,這樣就能夠解釋:
在Singleton中的__call__
方法對類屬性__instance
進行判斷:
__instance
爲None,代表類還未進行實例化,那麼給__instance
賦值爲元類的父類(type)的__call__
方法。__instance
不爲None,說明類已經進行過實例化,直接返回cls.__instance中的類實例。便實現了單例模式。
除了從新定義__call__
之外,元類能夠經過實現__init__
方法來定製實例,元類的__init__
方法能夠作到類裝飾器能作到的任務事情,而且做用更大。
若是想要進一步定製類,能夠在元類中實現__new__
方法。
另,編寫元類時,一般會把self參數改成cls,這樣能更清楚的代表要構建的實例是類。
元類的調用
上述例子中,都是經過metaclass=''
來設置類的元類,還能夠這樣:
class TestClass(): __metaclass__ = TestMetaClass def __init__(self, *args, **kwargs): print("class init") super().__init__()
在執行類定義時,解釋器會先尋找這個類屬性中的__metaclass__
,若是此屬性存在,就將這個屬性賦值給此類做爲它的元類,若是此屬性沒有定義的話,就會向上查找父類的__metaclass__
,若是沒有發現任何的父類,而且解釋器中也沒有名字爲__metaclass__
的全局變量,這個類就是傳統類,會使用type.ClassType做爲此類的元類。
以上。