五分鐘真的能理解python元類(Metaclasses)

[Python]五分鐘理解元類(Metaclasses)[Python]五分鐘理解元類(Metaclasses)
            五分鐘理解元類(Metaclasses)
真的,它並非巫術。
原文地址:http://www.voidspace.org.uk/python/articles/five-minutes.shtml
日期:16 September, 2008.
譯者:賴勇浩(http://blog.csdn.net/lanphaday)
“元類的魔幻變化比 99% 的用戶所擔心的更多,當你搞不懂是否真的需要用它的時候,就是不需要。”
—Tim Peters
本文源於在 PyCon UK 2008 上的一個快速演講。
元類被稱爲 Python 中的“深奧的巫術”。儘管你需要用到它的地方極少(除非你基於 zope 編程),可事實上它的基礎理論其實令人驚訝地易懂。
(a)一切皆對象
 一切皆對象,一切都有類型:“class”和“type”之間本質上並無不同,類也是對象,它們的類型是 type!以前,術語 type 用於內置類型,而術語 class 用於用戶定義的類,但自 Pythoon 2.2 以來“class”和“type”本質上並無不同。
對於舊風格(old-style)類的類型是 types.ClassType。
真的,這是真的
Python 2.5.1(r251:54869, Apr 18 2007, 22:08:04)
>>> class something(object):
pass


>>> something
<class '__main__.something'>
>>> type(something)
<type 'type'>
從這裏可以看出在交互式解釋器中創建的類是一個 first class 的對象。
類的類是-->它的元類
就像對象是類的實例一樣,類是它的元類的實例。
調用元類可以創建類。
確切來說,Python 中的其它對象也是如此。
因此當你創建一個類時……
解釋器會調用元類來生成它……
定義一個繼承自 object 的普通類意味着調用 type 來創建它:
>>> help(type)
Help on class type in module __builtin__:


class type(object)
 |  type(object) -> the object's type
 |  type(name, bases, dict) -> a new type

 |  

......

type 的第二種用法尤爲重要。當 Python 解釋器在執行一條類定義語句時(如例子中最初的兩行代碼之後),它會用下面的參數調用 type:
 字符串形式的類名 元組形式的基類序列——在我們的例子中是隻有一個元素的元組(’one-pl’)[1],如(object,)。 包括由名字影射的類成員(類屬性、方法等)的字典簡單模擬
>>> def __init__(self):

self.message = 'hello world!'

>>> def say_hello(self):
print self.message

>>> attrs = {'__init__':__init__, 'say_hello':say_hello}
>>> bases = (object,)
>>> hello = type('Hello', bases, attrs)
>>> h = hello()
>>> h.say_hello()
hello world!
以上代碼創建了類屬性的字典,然後調用 type 來創建了名爲 Hello 的類。
(b)__metaclass__ 的魔法
只要在類定義中把 __metaclass__ 設置爲任意有着與 type 相同參數的可調用對象,就能夠提供自定義的元類。
通常使用從 type 繼承的方法:
class PointlessMetaclass(type):
def __new__(meta, name, bases, attrs):
# do stuff...
return type.__new__(meta, name, bases, attrs)
重要的是在 __new__ 方法中我們能夠讀取或改變傳入的用以創建新類的參數。從而能夠內省屬性字典和改動、增加或者刪除成員。
儘管當實例化一個類時這兩個函數都會被調用,但覆蓋 __new__ 比 __init__ 更爲重要。__init__ 初始化一個實例,而 __new__ 的職責是創建它。因此如果元類用以自定義類的創建,就需要覆蓋 type 的 __new__。
使用新類而非僅僅提供工廠函數的原因在於如果使用工廠函數(那樣只是調用 type)的話元類不會被繼承。
In Action...
 class WhizzBang(object):
... __metaclass__ = PointlessMetaclass
...
 WhizzBang
&lt;class '__main__.WhizzBang'
 type(WhizzBang)
&lt;class '__main__.PointlessMetaClass'
WhizzBang 是一個類,但它現在已經不是 type 的實例,而是我們自定義的元類的實例了……
這有什麼用?
很好的問題,元類將用在創建使用了它的新類時調用,這裏是一些關於這樣做的好處的觀點:
 裝飾(Decorate)類的所有方法,用以日誌記錄或者性能剖分。 自動 Mix-in 新方法 在創建時註冊類。(例如自動註冊插件或從類成員創建數據庫模式。) 提供接口註冊,功能自動發現和接口適配。 類校驗:防止子類化,校驗所有的方法是否都有 docstrings。最重要之處在於元類中是在最後對 type 的調用時才真正創建類,所以可以自由地隨你喜歡地改變屬性字典(以及名稱和元組形式的基類序列)。
一些流行的 Python ORM(Object Relational Mappers(對象關係影射),用以和數據庫協同工作)也如此使用元類。
哦,還有因爲元類是繼承的,所以你能夠提供一個使用了你的元類的基類,而繼承自它的子類就無需顯式聲明它了。
但是……
我曾未需要使用它來編寫代碼……(我們用它來剖分,也在 Ironclad項目廣泛應用它,但我不編寫這些)。
還有,這一切只適用於 Python 2.x,其中的機制在 Python 3 中已經改變了。
type(type) is type
在 Python 2.6 中現在也可用使用? class decorators 來實現許多以前可能需要用元類來實現的東西。
最後,還有一個極盡奇技淫巧的例子(稍爲深入,但仍然不難消化),可以去看看 The Selfless Metaclass。它通過字節碼和方法簽名重寫來避免顯式地聲明 self 。
     [1]'one-pl'是指只有一個元素的元組。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章