到現在,我們已經知道元類別是什麼東東了。那麼,從頭到尾我們還不知道元類到底有啥用。只是了解了一下元類。在了解它有啥用的時候,我們先來了解下怎麼自訂元類別。因為只有了解了怎麼自訂才能更好的理解它的作用。
首先我們來了解下__metaclass__ 屬性
metaclass,直譯為元類,簡單的解釋就是:
當我們定義了類別以後,就可以根據這個類別建立出實例,所以:先定義類,然後再建立實例。
但是如果我們想建立出類別呢?那就必須根據metaclass建立出類,所以:先定義metaclass,然後再建立類別。
連接起來就是:先定義metaclass,就可以建立類,最後再建立實例。
所以,metaclass允許你建立類別或修改類別。換句話說,你可以把類別看成是metaclass創建出來的「實例」。
class MyObject(object): __metaclass__ = something… […]
如果是這樣寫的話,Python 就會用元類別來建立類別 MyObject。當你寫下 class MyObject(object),但類別物件 MyObject 還沒有在記憶體中建立。 Python 會在類別的定義中尋找 __metaclass__ 屬性,如果找到了,Python 就會用它來建立類別 MyObject,如果沒有找到,就會用內建的 type 函數來建立這個類別。如果還不怎麼理解,看下下面的流程圖:
再舉個實例:
class Foo(Bar): pass
它的判斷流程是怎麼樣的呢?
首先判斷 Foo 中是否有 __metaclass__ 這個屬性?如果有,Python 會在記憶體中透過 __metaclass__ 建立一個名字為 Foo 的類別物件(注意,這裡是類別物件)。如果 Python 沒有找到__metaclass__ ,它會繼續在 Bar(父類)中尋找__metaclass__ 屬性,並嘗試做和前面相同的操作。如果 Python在任何父類別中都找不到 __metaclass__ ,它就會在模組層次中去尋找 __metaclass__ ,並嘗試做同樣的操作。如果還是找不到 __metaclass__ ,Python 就會用內建的 type 來建立這個類別物件。
其實 __metaclass__ 就是定義了 class 的行為。類似於 class 定義了 instance 的行為,metaclass 則定義了 class 的行為。可以說,class 是 metaclass 的 instance。
現在,我們基本上了解了 __metaclass__ 屬性,但是,也沒講過如何使用這個屬性,或者說這個屬性可以放些什麼?
答案就是:可以創建一個類別的東西。那什麼可以用來創建一個類別呢? type,或任何使用到 type 或子類化 type 的東東都可以。
元類別的主要目的就是為了當建立類別時能夠自動地改變類別。通常,你會為API 做這樣的事情,你希望可以建立符合目前上下文的類別。假想一個很愚蠢的例子,你決定在你的模組裡所有的類別的屬性都應該是大寫形式。有好幾種方法可以辦到,但其中一種就是透過在模組層級設定__metaclass__ 。採用這種方法,這個模組中的所有類別都會透過這個元類別來創建,我們只需要告訴元類別把所有的屬性都改成大寫形式就萬事大吉了。
幸運的是,__metaclass__ 實際上可以被任意調用,它並不需要是一個正式的類別。所以,我們在這裡就先以一個簡單的函數作為例子開始。
# 元类会自动将你通常传给‘type’的参数作为自己的参数传入 def upper_attr(future_class_name, future_class_parents, future_class_attr): '''返回一个类对象,将属性都转为大写形式''' # 选择所有不以'__'开头的属性 attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) # 将它们转为大写形式 uppercase_attr = dict((name.upper(), value) for name, value in attrs) # 通过'type'来做类对象的创建 return type(future_class_name, future_class_parents, uppercase_attr) __metaclass__ = upper_attr # 这会作用到这个模块中的所有类 class Foo(object): # 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中 bar = 'bip' print hasattr(Foo, 'bar') # 输出: False print hasattr(Foo, 'BAR') # 输出:True f = Foo() print f.BAR # 输出:'bip' 用 class 当做元类的做法: # 请记住,'type'实际上是一个类,就像'str'和'int'一样 # 所以,你可以从type继承 class UpperAttrMetaClass(type): # __new__ 是在__init__之前被调用的特殊方法 # __new__是用来创建对象并返回之的方法 # 而__init__只是用来将传入的参数初始化给对象 # 你很少用到__new__,除非你希望能够控制对象的创建 # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__ # 如果你希望的话,你也可以在__init__中做些事情 # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用 def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return type(future_class_name, future_class_parents, uppercase_attr)
但是,這種方式其實不是 OOP。我們直接呼叫了 type,而且我們沒有改寫父類別的 __new__ 方法。現在讓我們這樣去處理:
class UpperAttrMetaclass(type): def __new__(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr): attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) # 复用type.__new__方法 # 这就是基本的OOP编程,没什么魔法 return type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)
你可能已經注意到了有一個額外的參數 upperattr_metaclass ,這並沒有什麼特別的。類別方法的第一個參數總是表示目前的實例,就像在普通的類別方法中的 self 參數一樣。當然了,為了清晰起見,這裡的名字我起的比較長。但是就像 self 一樣,所有的參數都有它們的傳統名稱。因此,在真實的產品程式碼中一個元類別應該是像這樣的:
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__') uppercase_attr = dict((name.upper(), value) for name, value in attrs) return type.__new__(cls, name, bases, uppercase_attr)
如果使用super 方法的話,我們還可以使它變得更清晰一些,這會緩解繼承(是的,你可以擁有元類,從元類繼承,從type 繼承)
class UpperAttrMetaclass(type): def __new__(cls, name, bases, dct): attrs = ((name, value) for name, value in dct.items() if not name.startswith('__')) uppercase_attr = dict((name.upper(), value) for name, value in attrs) return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
通常我們都會使用元類去做一些晦澀的事情,依賴於自省,控制繼承等等。確實,用元類來搞些「黑暗魔法」是特別有用的,因而會搞出些複雜的東西來。但就元類別本身而言,它們其實是很簡單的:
攔截類別的創建
修改類別
返回修改之後的類別
下一節