Python 什麼是元類(metaclasses)?

1.什麼是類

在理解元類以前,咱們必須先掌握Python中的類(class)。php

和大多數語言同樣,Python中的類知識用來描述如何「生成一個對象」:python


可是,在Python中,類不只能用來描述如何生成一個對象,類自己也是對象數據庫

在你使用關鍵詞 class 的時候,Python就會執行它,並建立一個對象。數組

>>> class ObjectCreator(object):...  pass...


上述指令在內存中建立了一個「ObjectiveCreator」的對象。ruby

這個對象(類)自己具備建立對象(實例)的能力,所以它也是一個類。你能夠對它作如下操做:bash

1.將其分配給變量
2.複製它
3.爲其添加屬性
4.將其做爲函數參數傳遞微信

例如:app

2.動態建立類

因爲類是對象,所以你能夠像建立任何對象(數組、字典等)同樣,隨時隨地建立類。函數

你甚至能夠在函數裏建立類:優化



可是,這樣的類並非很動態,由於你必須本身編寫整個類。

使用class關鍵字時,Python會幫你自動建立此對象,可是,Python一樣也提供了一種手動建立的方法,那就是type函數。

>>> print(type(1))<type 'int'>>>> print(type("1"))<type 'str'>>>> print(type(ObjectCreator))<type 'type'>>>> print(type(ObjectCreator()))<class '__main__.ObjectCreator'>


type函數最經典的用法是返回對象的類型。可是不多人知道,它還能接受參數並手動建立類。

type(name, bases, attrs)


其中

  • name: 類名

  • bases: 元組,父類名

  • attrs: 字典,類屬性值

所以你能夠這樣手動建立類:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object>>> print(MyShinyClass)<class '__main__.MyShinyClass'>>>> print(MyShinyClass()) # create an instance with the class<__main__.MyShinyClass object at 0x8997cec>


若是你想給它賦予屬性,能夠這樣玩:

>>> class Foo(object):...  bar = True


等同於

>>> Foo = type('Foo', (), {'bar':True})


用來繼承也是能夠的:

>>> FooChild = type('FooChild', (Foo,), {})>>> print(FooChild)<class '__main__.FooChild'>>>> print(FooChild.bar) # bar is inherited from FooTrue


可見經過 type() 函數建立的類和直接寫class是徹底同樣的。

由於Python解釋器遇到class定義時,僅僅是掃描一下class定義的語法,而後調用 type() 函數建立出class。

正常狀況下,咱們用class來定義類,可是,type()函數也容許咱們動態建立類,也就是說,動態語言自己支持運行期動態建立類,這和靜態語言有很是大的不一樣。

Python是經過什麼作到這一切的?那就是元類。

3.什麼是元類

元類就是用於建立類的「東西」。

你定義類是爲了建立對象,Python中全部的類都是對象。元類是用於建立這些對象的。能夠看這個例子:

MyClass = MetaClass()my_object = MyClass()


這有點像套娃。這段代碼轉化爲type就是這樣的:

MyClass = type('MyClass', (), {})


所以,咱們能夠獲得一個基本事實,type 自己就是一個元類

其實,就是 type 在幕後建立了Python中全部的類。

經過檢查__class__屬性,你會看到Python中,一切對象都是基於 type 的:

>>> age = 35>>> age.__class__<type 'int'>>>> name = 'bob'>>> name.__class__<type 'str'>>>> def foo(): pass>>> foo.__class__<type 'function'>>>> class Bar(object): pass>>> b = Bar()>>> b.__class__<class '__main__.Bar'>


那麼,有個有趣的問題,__class__的__class__是什麼呢?

>>> age.__class__.__class__<type 'type'>>>> name.__class__.__class__<type 'type'>>>> foo.__class__.__class__<type 'type'>>>> b.__class__.__class__<type 'type'>


所以,元類只是建立類對象的東西,若是願意,能夠將其稱爲「類的工廠」。

type 是Python使用的內置元類。不過,你能夠建立本身的元類。

3.1 __metaclass__屬性

在Python 2中,能夠在編寫類時添加屬性__metaclass__,使用某個元類來建立該類:

class Foo(object): __metaclass__ = something... [...]


不過要當心的是,你雖然先寫了 class Foo(object),但Foo這個對象還沒有被建立,Python將先尋找__metaclass__類,找到後用它來建立Foo類。

若是沒有這個__metaclass__類,它將使用 type 來建立類。

所以,類建立的流程是這樣的:

1.建立的類中有__metaclass__元類屬性嗎?

2.若是有,那就用__metaclass__給該類在內存中建立一個類對象。

3.若是Python找不到__metaclass__,它將在MODULE級別查找__metaclass__屬性 。

4.若是仍是沒有,那就使用父類的元類來建立類對象。

如今的問題就是,你能夠在__metaclass__中放置些什麼代碼呢?

答案就是:能夠建立一個類的東西。那麼什麼能夠用來建立一個類呢?type,或者任何繼承或使用它的東西。

3.2 Python 3中的元類

設置元類的語法在Python3已改成:

class Foo(object, metaclass=something): ...


即再也不使用__metaclass__屬性,而是在基類參數列表中引入關鍵字參數。

不過元類的基本工做方式不變。在Python3中,你能夠將屬性做爲關鍵字參數傳遞給元類:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2): ...

4.爲何須要元類

元類最主要的一個應用方向是建立API,一個最著名的應用是Django ORM,好比:

class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField()


當你這樣訪問屬性的時候:

person = Person(name='bob', age='35')print(person.age)


它並不會返回models.IntegerField,而是返回了一個整形的數字。

這是由於models.Model引用了一個ModelBase類,該類隨後進行了魔術般地操做,使其可以與數據庫字段進行掛鉤。

這就是元類的做用,Django經過它,完成了系列複雜的幕後工做,將本來很是複雜的事情變得很是簡單。

本文翻譯自stackoverflow,有部分優化修改:
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python

Python實用寶典 (pythondict.com)
不僅是一個寶典
歡迎關注公衆號:Python實用寶典

本文分享自微信公衆號 - Python實用寶典(pythondict)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索