Python 多继承下 metaclass 优先级

在 Python 里面,class 也是一个对象,它是 type 实例化后生成的对象。 我们一般用如下的代码来定义一个类。

class ClassA(object):
  def method1(self):
    pass

  @classmethod
  def method2(cls):
    pass

实际上 class 这个关键字是类似语法糖一样的东西。 上面的 class 定义等价为

def method1(self):
  pass

@classmethod
def method2(cls):
  pass

attrs = {
  'method1': method1,
  'method2': method2,
}

ClassA = type('ClassA', (object,), attrs)

type 的三个参数分别为:

  • 类的名字( ClassA.__name__ 中存储的名字),

  • 继承的父类(可以是多个父类),

  • 类的所有属性(类的各种方法和属性)。

以上是一个未指定 __metaclass__ 的 class 的生成过程。 但是在 ORM 的代码中,经常可以看到类似这样的写法

class MetaA(type):
  def __new__(cls, name, bases, attrs):
    # some work
    new_class = xxx
    return new_class

class ClassB(object):
  __metaclass__ = MetaA

  def create_xxx(self):
    pass

ClassB 的属性中,比之前多了一个 __metaclass__ 的属性。__metaclass__ 是做什么用的呢? 我们先把这个类用 type(name, bases, attrs) 的方式重新写下。

class MetaA(type):
  def __new__(cls, name, bases, attrs):
    # some work
    new_class = xxx
    return new_class

def create_xxx(self):
  pass

attrs = {
  'create_xxx': create_xxx,
}

ClassB = MetaA('ClassB', (object,), attrs)

__metaclass__ 的属性就是替换 type 的。 __metaclass__ 的默认值就是 type。 那如果子类从指定了 __metaclass__ 的父类继承,而子类未指定 __metaclass__ 会怎么样呢?

class ClassC(ClassB):
  pass

通过实现,我们可以得知 ClassC 会用 ClassB__metaclass__ 属性来生成自己。

Important

当一个类自己没有定义 __metaclass__ 的情况下,它会去父类找 metaclass , 如果父类没有,就去父类的父类找,一直到找到为止。如果所有到父类都没有 指定 __metaclass__ ,则使用默认的 type 来构造。

我们还有有一个疑问: 如果父类指定了 __metaclass__ , 父类的父类也指定了 __metaclass__;或者父类继承了多个父类, 这多个父类都有自己的 __metaclass__ ,那会发生什么情况?

对于这一点,Python 对 __metaclass__ 有一个限制, 就是一个类的 __metaclass__ 和它所有父类的所有 __metaclass__ 必须有继承关系或者是同一个 (参考Python源码) 。 在这个基础上,真实使用的 __metaclass__ 是继承链的最底端,既是最子类的那个 metaclass。

Important

一个类的 __metaclass__ 和它所有父类的 __metaclass__ 必须有继承关系或者是同一个