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__方法