python黑魔法之metaclass

最近了解了一下python的metaclass,在學習的過程當中,把本身對metaclass的理解寫出來和你們分享。python

 

首先, metaclass 中文叫元類,這個元類怎麼來理解呢。咱們知道,在Python中,一切都是對象。咱們定義一個類,而後實例化,獲得了一個類的實例對象。咱們能夠把類理解成建立實例對象的模板。其實,這個模板,也就是類自己,也是一個對象。既然類也是對象,那麼咱們就能夠對他進行不少操做。好比,把類做爲函數的參數,建立類的引用等等。那麼建立類的模板,就是元類。函數

 

在Python中,咱們很早就接觸了一個函數 type,用來返回對象的類型。好比學習

In [1]: print(type(1))
<type 'int'>
In [2]: print(type("1"))
<type 'str'>
In [3]: def foo():pass
In [4]: print (type(foo))
<type 'function'>
In [5]: class Foo():pass
In [6]: print(type(Foo()))
<type 'instance'>
In [7]: print(type(Foo))
<type 'classobj'>

其實 type函數還有另一個用處,那就是用來建立類,用法就是spa

type(classname,(superclassname,),{attrs}),咱們來看一下:code

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

其實等效於對象

class MyClass():
    pass

咱們說過,metaclass是建立類的類,因此,type就是一個metaclass。咱們來驗證一下blog

In [23]: age.__class__
Out[23]: int
In [24]: name.__class__
Out[24]: str
In [25]: MyClass().__class__
Out[25]: __main__.MyClass
In [26]: MyClass().__class__.__class__
Out[26]: type
In [29]: age.__class__.__class__
Out[29]: type
In [30]: name.__class__.__class__
Out[30]: type

type是內置的metaclass,咱們也能夠本身定義本身的元類。在定義類的時候,定義 __metaclass__屬性,這種狀況下,Python就會使用metaclass 來建立類。具體模式以下:it

在建立類時,Python會尋找有沒有定義__metaclass__,若是有,就用定義好的metaclass來建立類。io

若是在當前類找不到 __metaclass__,Python會在當前模塊下尋找__metaclass__function

若是仍是找不到,Python會尋找當前類第一個父類的__metaclass__,直到最後的內建函數type()

 

那麼__metaclass__定義究竟是什麼呢?

是任何可以建立類的,好比type或者type的子類

 

因此metaclass的目的在類建立的時候自動的修改類,舉個例子,咱們想把一個類裏全部的屬性前面都加一個my_前綴,使用metaclass的方式就比較簡單

class MyAttrMetaclass(type):
    def __new__(cls, clsname, bases,dct):
        my_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                my_attr["my_"+name] = val
            else:
                my_attr[name] = val
        return type.__new__(cls, clsname, bases, my_attr)


class Foo():
    __metaclass__= MyAttrMetaclass
    test_age = 1
    test_name = "test"

print(hasattr(Foo,"test_age"))
False
print(hasattr(Foo,"my_test_age"))
True

能夠看到經過元類的方式,咱們的類Foo的屬性被修改了。

 

最後一個問題就是咱們在什麼狀況下會用到metaclass呢?

常見的例子有Django ORM 的Model類,

咱們定義一個Person類集成Model

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

p = person(name="haha",age="31")

p.age 返回的是int 而不是IntegerField(),這就是由於在Model中使用的metaclass動態修改類

 

本文總結了黑魔法metaclass的定義,用法,主要參考了http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 

相關文章
相關標籤/搜索