python中的__metaclass__

來源:互聯網
上載者:User

標籤:

什麼是元類:

  python中類也是一種對象, 可以稱為類對象.

  元類就是用來建立類對象的"東西". 你建立類就是為了建立類的執行個體對象, 不是嗎? 但是我們已經學習了python中的類也是對象. 元類就是用來建立這些類對象的, 元類就是類的類, 你可以這樣理解:

MyClass = MetaClass()MyObject = MyClass()

你已經看到了type可以這樣來動態建立類:

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

這是因為type實際上是一個元類. type就是python在背後用來建立所有類的元類. 那麼現在你肯定特別想知道type既然是一個類(元類), 為什麼首字母沒有大寫? 好吧, 我猜這是為了與int, str, function這些python內建資料類型保持一致, str是用來建立字串對象的類, 而int是用來建立整數對象的類, type就是用來建立類對象的類. 你可以通過檢查__class__屬性來證明這一點. python中所有的東西都是對象(這一點和javascript相同), 這包括整數, 字串, 函數以及類, 它們全部都是對象, 而且它們都是從一個類建立而來, 這個類就是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‘>>>> Bar.__class__<type ‘type‘>

從上面結果可以看出, python中的所有普通的類也是對象, 當然這些類對象也有類型. 可以看出, int, str, 函數, 普通自訂類, 這些類對象的類型都是type.

  元類就是建立類對象的類. 如果你喜歡, 可以把元類看做"類工廠". type就是python的內建元類, 當然也可以建立自己的元類.

__metaclass__屬性:

  你可以在實現一個類的時候為其添加__metaclass__屬性:

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

如果你這麼做了, python就會用元類來建立類Foo. 當在記憶體中建立類對象Foo時, python會在類的定義中尋找__metaclass__屬性, 如果找到了, python就用它來建立Foo, 如果沒有找到, 就會用內建的type元類來建立這個類.

當你實現一個有繼承的類時:

class Foo(Bar):    pass

python會做如下操作:

Foo中有__metaclass__這個屬性嗎? 如果是, python會在記憶體中通過__metaclass__建立一個名字為Foo的類對象. 如果python沒有找到__metaclass__, 它會繼續在Bar(父類)中尋找__metaclass__, 以此類推. 如果python在任何父類中都沒有找到__metaclass__, 它就會在模組層次中尋找__metaclass__. 如果還是找不到__metaclass__, python就會用內建的type元類來建立這個類對象.

自訂元類:

  元類的主要目的是為了當建立類時能夠自動的改變類, 或者說定製類. 為什麼這麼說呢, 我們可以這樣理解: 把類的建立過成想象成一個管道, 如果遍曆完繼承鏈+模組層次都沒有發現__metaclass__屬性, 那麼用type元類來建立該類. 這種情況下我們在類中怎樣定義屬性, 最終產生的類對象也會具有怎樣的屬性, 如所示:

類建立時的樣子---(type)-->類最終的樣子(由於是用type元類, 所以類最終樣式與類建立時樣式一樣)

如果遍曆過程中找到了__metaclass__屬性, 那麼就會用自訂元類來建立該類, 如所示:

類建立時的樣子---(__metaclass__屬性指向的元類)--->類最終的樣子

由於在我們自訂的元類裡面可以定製那些__metaclass__指過來的類, 因此元類的主要目的一般是為了當建立類時自動的改變類, 或者說定製類.

  到底什麼時候才會用到元類呢? 通常你會為API做這樣的事情, 你希望可以建立複合當前內容相關的類. 假想一個很傻的例子, 你希望在你的模組裡所有類的屬性都應該是大寫形式. 有好幾種方法可以辦到, 但其中一種就是通過在模組層級別設定__metaclass__. 採用這種方法, 這個模組中的所有類都會通過這個元類來建立, 我們只需要告訴元類把所有的屬性都改成大寫形式就行:

# !/usr/bin/python# -*- coding: utf-8 -*-# type實際上是一個類, 就像str和int一樣, 所以你可以從type繼承class UpperAttrMetaClass(type):    def __new__(cls, name, bases, dct):        attrs = ((name, value) for name, value in dct.items() if not name.startswith(‘__‘))        uppercase_attrs = dict((name.upper(), value) for name, value in attrs)        return type.__new__(cls, name, bases, uppercase_attrs)class Foo(object):    __metaclass__ = UpperAttrMetaClass    bar = ‘bip‘print hasattr(Foo, ‘bar‘)# 輸出falseprint hasattr(Foo, ‘BAR‘)# 輸出truef = Foo()print f.BAR# 輸出bip

也可以使用super方法:

class UpperAttrMetaClass(type):    def __new__(cls, name, bases, dct):        attrs = ((name, value) for name, value in dct.items() if not name.startswith(‘__‘))        uppercase_attrs = dict((name.upper(), value) for name, value in attrs)        #return type.__new__(cls, name, bases, uppercase_attrs)        return super(UpperAttrMetaClass, cls).__new__(cls, name, bases, uppercase_attrs)

就是這樣, 除此之外關於元類真的沒有別的可以說的了. 就元類本身而言, 它其實很簡單:

1. 攔截類的建立

2. 修改類(或者說定製類)

3. 返回修改之後的類

究竟為什麼要使用元類:

元類的主要作用是建立API. 一個典型的例子是Django ORM. 它允許你像這樣定義類:

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

 但是如果你像這樣做的話:

guy = Person(name = "bob", age = "35")print guy.age

調用guy.age並不會返回一個IntegerField對象, 而是會返回一個int, 是指可以直接從資料庫中取出資料. 這是有可能的, 因為models.Model定義了__metaclass__, 並且在元類中將你剛剛定義的簡單的Person類給修改了, 變的能夠訪問資料庫了. Django架構將這些看起來很複雜的東西通過暴露出一個簡單的使用元類的API(models.Model)將其化簡, 通過這個API重新建立代碼, 在背後完成真正的工作.

結語:

  現在我們知道了, 類本身也是執行個體, 只不過它們是元類的執行個體. python中的一切都是對象, 這些對象要麼是類對象的執行個體, 要麼是元類的執行個體, 除了type. 元類是比較複雜的, 大多數情況下, 我們不會對類做修改. 如果需要對類做修改,  大多數情況下我們會通過下面兩種技術來動態修改類:

1. Monkey patching

2. class decorators

 

python中的__metaclass__

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.