上一篇解決了經過調用類對象生成實例對象過程當中可能遇到的命名空間相關的一些問題,此次咱們向上回溯一層,看看類對象自己是如何產生的。python
咱們知道 type()
方法能夠查看一個對象的類型,或者說判斷這個對象是由那個類產生的:編程
print(type(12)) print(type('python'))
<class 'int'> <class 'str'>
class A: pass print(type(A))
<class 'type'>
經過這段代碼能夠看出,類對象 A
是由type()
產生的,也就是說 type
也能夠用來產生新的對象,並且產生的是類對象,所以它是全部類對象的類:app
print(type.__doc__)
type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type
class
定義類的語法實際上轉化爲 type(name, bases, dict)
,其中 name
參數爲類的名字,bases
爲繼承父類的元組,dict
爲類的屬性和方法:code
class A: pass # 實際上等於 B = type('A', (), {}) print(A.__name__ == B.__name__)
True
理論上說這就是元類的意義,但從實際的角度出發顯然使用 class
語法更方便、合理,而元類的實際意義則是經過繼承 type
類來構造一個新的元類,並進行特定的操做以產生具備特定行爲的類對象。這樣看來它的本質與普通的類對象沒有差別,只不過繼承的是 type
類。orm
在生成實例時是經過調用 __init__
方法進行初始化的,而實際上在此以前會先調用 __new__
方法用於建立實例,再經過 __init__
初始化,就好像 __new__
負責聲明變量,而 __init__
負責對聲明的變量進行初始化同樣。這裏有一個規則是 __new__(cls,)
的返回值必須是 cls
參數的實例,不然 __init__
將不會觸發,例如在 enum.Enum
的定義中,因爲枚舉類型是單例模式,所以在定義 __new__
的時候沒有返回其實例,也就不會進行初始化:對象
class Enum: def __new__(cls, value): print(cls, value) return value def __init__(self): print("Will not be called!") e = Enum(1)
<class '__main__.Enum'> 1
一般狀況下本身定義 __new__
須要經過調用父類的 __new__
方法建立一個 cls
的實例,一樣在定義元類的時候則是調用上面提到的 type
的用法(由於元類繼承自 type
):繼承
class MetaEnum(type): def __new__(metaclass, name, base, attrs): print("Metaclass: {}\nName: {}\nParents: {}\nAttributes: {}".format(metaclass, name, base, attrs)) return super().__new__(metaclass, name, base, attrs)
class Enum(metaclass=MetaEnum): # Python 2.7 中定義元類的方法是使用 __metaclass__ 變量 # [PEP 3115](https://www.python.org/dev/peps/pep-3115/) # 將 Python 3.0 之後語法改成 class Cls(metaclass=Meta) test = 0
Metaclass: <class '__main__.MetaEnum'> Name: Enum Parents: () Attributes: {'__qualname__': 'Enum', '__module__': '__main__', 'test': 0}
此時咱們再來看 Enum
的類,已經再也不是 type
而是其元類 MetaEnum
:圖片
type(Enum)
__main__.MetaEnum
除了 __new__
方法以外,PEP 3115 還定義了 __prepare__
屬性,用於設定初始化的命名空間(即 type
的第 3 個參數),仍是以 enum.Enum
爲例,咱們須要限制枚舉類型中屬性名稱不得重複使用,則能夠經過元類限制類的行爲:get
# 定義新的字典類,在賦值新的 dict[k] = v 時 # 檢查 k 是否重複 class _EnumDict(dict): def __init__(self): super().__init__() self.members = [] def __setitem__(self, k, v): if k in self.members: raise TypeError("Attempted to reuse key: '{}'".format(k)) else: self.members.append(k) super().__setitem__(k, v) class MetaEnum(type): @classmethod def __prepare__(metaclass, cls, bases): return _EnumDict() def __new__(metaclass, name, base, attrs): return super().__new__(metaclass, name, base, attrs) class Enum(metaclass=MetaEnum): pass class Color(Enum): try: red = 1 red = 2 except TypeError:# 這裏沒有使用 as err: 的緣由是? print("TypeError catched")
TypeError catched
Python 中一切皆爲對象,全部的對象都是某一類的實例,或是某一元類的實例,type
是本身的元類也是本身的實例:it
元類在 Python 中屬於比較深層的黑魔法,在通常的平常應用中可能並不經常使用,但理解其背後的原理對於理解 Python 面向對象編程以及一切皆爲對象的理念頗有幫助;若是你須要對類進行深度改造,至少要知道從何入手。