python中元類在創建類和實例的作用

python中元類在創建類和實例的作用

最近在研究backtrader的底層源代碼,作者用類若干的類和元類,以前沒有接觸過元類,現在經過查了一些資料,知道了元類是創建類的類,可以用來控制類的創建和實例的創建過程,也可以隱性地繼承屬性和方法,元類的常見的應用有創建單類模式、ORM
由於元類的理解比較抽象,可以用下面的代碼理解元類在創建類和實例時的作用,

  • metaClass1: 繼承自type類的元類
  • metaClass2: 繼承自metaClass1的元類
  • A: 由metaClass1創建的類
  • M:繼承自A的子類
  • a: 類A的實例化對象
  • m: 類M的實例化對象
class metaClass1(type):
    def __new__(cls, clsname, bases, dct):
        print("基於type的類metaClass1,....__new__")
        return type.__new__(cls, clsname, bases, dct)

    def __call__(self, *args, **kwargs):
        print("基於type的類metaClass1,....__call__")
        return super().__call__(*args, **kwargs)


class metaClass2(metaClass1):
    def __new__(cls, clsname, bases, dct):
        print("基於metaClass1的類metaClass2,....__new__")
        return super().__new__(cls, clsname, bases, dct)

    def __call__(self, *args, **kwargs):
        print("基於metaClass1的類metaClass2,....__call__")
        return super().__call__(*args, **kwargs)

print("創建類A-------")

class A(metaclass=metaClass2):
    def __new__(cls, *agrs, **kwds):
        inst = super().__new__(cls, *agrs, **kwds)
        print("由metaClass2創建的類A,....__new__...正在創建類的實例")
        return inst

    def __init__(self):
        print("由metaClass2創建的類A,....__init__...正在創建類的實例")
        super().__init__()


class metaClass3(type):
    def __new__(cls, clsname, bases, dct):
        print("基於metaClass1的類metaClass2,....__call__")
        return super().__new__(cls, clsname, bases, dct)

print('創建A的子類M-------')
class M(A, metaclass=type):

    def __new__(cls, *agrs, **kwds):
        print("類A的子類M,...__new__...正在創建類的實例")
        return super().__new__(cls, *agrs, **kwds)

    def __init__(self):
        print("類A的子類M,...__init__...正在創建類的實例")
        super().__init__()

print("創建A的實例--------")
a = A()
print("創建M的實例--------")
m = M()

輸出

創建類A-------
基於metaClass1的類metaClass2,....__new__
基於type的類metaClass1,....__new__

創建A的子類M-------
基於metaClass1的類metaClass2,....__new__
基於type的類metaClass1,....__new__

創建A的實例--------
基於metaClass1的類metaClass2,....__call__
基於type的類metaClass1,....__call__
由metaClass2創建的類A,....__new__...正在創建類的實例
由metaClass2創建的類A,....__init__...正在創建類的實例

創建M的實例--------
基於metaClass1的類metaClass2,....__call__
基於type的類metaClass1,....__call__
類A的子類M,...__new__...正在創建類的實例
由metaClass2創建的類A,....__new__...正在創建類的實例
類A的子類M,...__init__...正在創建類的實例
由metaClass2創建的類A,....__init__...正在創建類的實例

分析

這裏由兩條關係:父類-子類繼承關係、元類-類創建關係。

創建類

通過元類創建類時,子類M不必另外聲明元類,因爲創建M時,會默認調用父類A的元類,如果設置metaclass爲父類的元類外的別的元類,程序會出錯。用元類創建類時,依次調用元類的__new__方法,該方法的參數分別爲:

  • cls:類對象,類似class中的self
  • clsname: 類名,就像例子中的“A”、“M”
  • bases:類的父類
  • dct:類的屬性,是一個字典

通過這些參數可以看出,這是在得到類的屬性後纔會進入到元類中。

創建實例

創建實例涉及到類的__init____new__和元類的__call__

  1. __call__:首先調用父類的該方法,如果元類有繼承關係,先依次(子元類->父元類)調用完所有元類的__call__方法,接着調用類的__new__方法
  2. __new__:調用完元類的__new__方法後,接着調用類的__new__,如果類有繼承關係,則依次(子類->父類)調用完所有類的__new__方法
  3. __init__:調用完所有類的__new__方法後,接着調用類的__init__,如果有類的繼承關係,則依次(子類->父類)調用所有類的__init__方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章