2019新年快樂 - 學習python的“元類”
1學習的原文:What are metaclasses in Python
時刻牢記:python是面向物件的,才能理解python的“元類”
理解python元類的概念之前,一定要時刻牢記python是面向物件的,也就是說,python中,一切都是物件,整數是物件、字串是物件、函式也是物件,類的例項更是物件。
>>> age = 47
>>> type(age)
<class 'int'>
>>> age.__class__
<class 'int'>
>>> city = 'shenyang'
>>> city.__class__
<class 'str'>
>>> type(city)
<class 'str'>
>>>
python中,類(class)也是物件!
class ObjectCreator(object):
... pass
當python直譯器看到定義類的關鍵字“class”時,在記憶體中建立一個名稱為類名(ObjectCreator)的“物件”,這個物件(類)自身能建立一個物件(這個類的例項),因此這也就證明了這個“物件”就是個“類”。而且因為這個 “類”同時也是個物件,所以這個物件(類)可以:
- 被賦值給一個變數
- 可以複製(copy)
- 可以給它增加屬性
- 可以作為一個函式的引數被傳遞
- 類的定義程式碼不一定非得寫在“外面”,它可以寫在函式內部!
上面這段話有些繞嘴,但它是理解“元類”這個概念的基礎:python中的類不僅僅是一段定義類的程式碼,它本身就是存在於記憶體中的一個物件,這個物件可以被用來建立其它的類(子類)。
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo #return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print (MyClass)
<class '__main__.choose_class.<locals>.Foo'>
>>> MyClass = choose_class('bar')
>>> print (MyClass)
<class '__main__.choose_class.<locals>.Bar'>
>>> print (MyClass())
<__main__.choose_class.<locals>.Bar object at 0x108dea4e0>
那麼,問題是:既然類是物件,那誰來建立這個物件?這個物件有沒有父類?
答案是:type產生類!
“type”函式除了能返回“物件”的“類”型之外。還是python用來動態產生“類”的函式。語法如下:
type(name of the class,
tuple of the parent class (for inheritance, can be empty),
dictionary containing attributes names and values)
例如,下面這個類,通常的定義是:
>>> class MyShinyClass(object):
... pass
使用type而不是上面的程式碼產生MyShinyClass類的方法如下:
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print (MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x108dea2e8>
>>>
也就是說,python直譯器將類定義的程式碼動態的用type在記憶體中生成一個物件。
元類 metaclass
閱讀如上所述的python建立類的過程可以得出結論:“類”也是個物件,那這個物件肯定是某個類的例項(物件),而這個“某個類”就是所謂的“元類(metaclass)”,也就是說,元類是所有類的類,是所有類的“父類”,這個元類(metaclass)就是“type”!“type”就是python的內嵌的“元類(metaclass)”!
>>> age = 45
>>> age.__class__
<class 'int'>
>>> name = 'bob'
>>> name.__class__
<class 'str'>
>>> def foo(): pass
...
>>> foo.__class__
<class 'function'>
>>> class Bar(object): pass
...
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>
>>> age.__class__.__class__
<class 'type'>
>>> name.__class__.__class__
<class 'type'>
>>> foo.__class__.__class__
<class 'type'>
>>> b.__class__.__class__
<class 'type'>
>>>
看到了吧,type就是python所有物件(類)的“元類”,它定義了python所有類(內建或自定義)建立時所需做的預設操作,也正是因為這樣,python中可以自定義“元類”。
建立自己的元類
建立自己的元類的目的,是增加一些“在類建立時的預設操作”。
python2中自定義元類的程式碼:
class Foo(object):
__metaclass__ = something...
這個something可以是anything,通常是一個完成特定功能的函式。
python3中自定義元類的程式碼:
class Foo (object, metaclass = something):
[...]
注意:
原文中有這麼一段話:
Be careful here that the metaclass attribute will not be inherited, the metaclass of the parent (Bar.class) will be. If Bar used a metaclass attribute that created Bar with type() (and not type.new()), the subclasses will not inherit that behavior.
這段話字面意思是:__metaclass__這個,“特性”是不能被繼承的,能被繼承的是這個類的父類的元類。這句話也有些“混沌”,大概的意思就是:__metaclass__不是一個公開的可以被子類繼承的“屬性”,它只是這個“類”用來實現自定義“元類”的一種實現手段。或者乾脆這麼理解:__metaclass__不是面向物件程式設計概念中的“屬性(property)”,是父類的私有特性。