一、概述
Python雖然是多范式的編程語言,但它的數據模型卻是 純面向對象 的。與那些僅在語法層面聲稱純OO的編程語言(如Java)相比,Python的這種純粹性更加深入骨髓。
在Python的世界里,一切皆為對象:數值、序列、字典、函數、模塊、文件、類、類實例 等等,無一例外(參考 Data model)。其中,“類也是對象” 的概念最讓人匪夷所思,這完全超越了傳統的OO思想。
元類(metaclass)是Python 2.2中引入的概念,利用元類可以 定制類的創建行為(Customizing class creation)。“元類” 的概念同樣讓人難以理解,然而理解 “元類” 是理解 “類也是對象” 的關鍵。
二、經典闡述
對於元類的理解,目前為止,最經典的闡述莫過於Stack Overflow上面的這篇帖子 What is a metaclass in Python?,e-satis 神一般的回復讓人醍醐灌頂,看完后基本就了然於胸了。
如果覺得看英文比較吃力,這里有一篇中文譯版 深刻理解Python中的元類(metaclass)(注:英文原版最近有更新,但核心內容不變)。
三、核心總結
1、類的創建過程
對於類定義:
class Foo(Base):
def say(self):
print 'hello'
Python解釋器 執行class語句 時,處理步驟如下:
-
確定元類
mcls。元類的查找優先級為:- 首先查找 類Foo 是否擁有屬性
__metaclass__ - 否則查找 類Foo的父類 是否具有屬性
__metaclass__ - 否則查找 類Foo所在模塊 是否具有全局變量
__metaclass__ - 否則使用默認元類(經典類:
types.ClassType;新式類:type)
- 首先查找 類Foo 是否擁有屬性
-
使用元類
mcls創建類Foo。創建語意等價於:def say(self): print 'hello' # 元類的參數:mcls(name, bases, dict) Foo = mcls('Foo', (Base,), {'say': say}) -
創建成功后,類
Foo是 元類mcls的 實例。
綜上:創建類 其實是一種更高級別的 實例化過程,本質上與 創建類實例 相似。
| 實例化過程 | 類 | 實例 | 語意形式 |
|---|---|---|---|
| 創建類Foo | 元類mcls | 類Foo | class Foo: pass <=> Foo = mcls('Foo', (), {}) |
| 創建類實例foo | 類Foo | 類實例foo | foo = Foo() |
2、元類的使用慣例
原則上,元類可以是:任何接受參數 name, bases, dict 並返回 類 的 可調用對象(參考 metaclass)。
例如元類可以是 函數:
def metacls_func(name, bases, dict):
# do customizing here
return type(name, bases, dict)
根據最佳實踐指導,更好的習慣是使用 類 作為元類,典型風格如下:
class MetaCls(type):
def __new__(cls, name, bases, dict):
# do customizing here
return super(MetaCls, cls).__new__(cls, name, bases, dict)
注意:
- 元類可以繼承自另一個元類,也可以使用其他元類
- 除了常用的
__new__,還可以借助__init__和__call__來定制被創建的類
四、簡單案例
1、默認行為
1)經典類(Classic classes)
類Old的三種等價定義:
class Old: pass
class Old:
__metaclass__ = types.ClassType
Old = types.ClassType('Old', (), {})
類Old是元類types.ClassType的實例:
>>> isinstance(Old, types.ClassType)
True
2)新式類(New-style classes)
類New的三種等價定義:
class New(object): pass
class New:
__metaclass__ = type
New = type('New', (), {})
類New是元類type的實例:
>>> isinstance(New, type)
True
2、使用元類
為所有類打上作者標簽:
class AuthorTag(type):
def __new__(cls, name, bases, dict):
dict['__author__'] = 'RussellLuo'
return super(AuthorTag, cls).__new__(cls, name, bases, dict)
class MyBlog:
__metaclass__ = AuthorTag
class MyGitHub:
__metaclass__ = AuthorTag
現在,類MyBlog和類MyGitHub都免費獲得了作者簽名:
>>> MyBlog.__author__
'RussellLuo'
>>> MyGitHub.__author__
'RussellLuo'
五、實踐為王
請記住上面的簡單案例,如果您想從本文中獲得對Python元類僅有的一點印象。紙上得來終覺淺,絕知此事要躬行,開始吧。
