All Sunday

首页

Python多继承下metaclass优先级

在 Python 里面,class 也是一个对象,它是 type 实例化后生成的对象。我们一般用

class ClassA(object):
  def method1(self):
    pass
  
  @classmethod
  def method2(cls):
    pass

可以定义类

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

def method1(self):
  pass
  
@classmethod
def method2(cls):
  pass
  
attrs = {
  'method1': method1,
  'method2': method2,
}

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

1
type
的三个参数分别表示 类的名字(
1
ClassA.__name__
中存储的名字), 继承的父类(可以是多个父类), 类的所有属性(类的各种方法和属性)。

以上是一个不指定

1
__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 的属性中,比之前多了一个

1
__metaclass__
的属性。
1
__metaclass__
是做什么用的呢? 我们先把这个类用
1
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)

到这里,你一定看明白了,

1
__metaclass__
的属性就是替换
1
type
的。
1
__metaclass__
的默认值就是
1
type

那如果一个类从某个指定了

1
__metaclass__
的类继承,而它自身没指定
1
__metaclass__
会怎么样呢?

class ClassC(ClassB):
  pass

我想你一定猜到了,

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

你一定会有一个疑问,如果父类指定了

1
__metaclass__
, 父类的父类也指定了
1
__metaclass__
;或者父类继承了多个父类, 这多个父类都有自己的
1
__metaclass__
,那会发生什么情况?

对于这一点,Python 对

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

下一篇: Linux磁盘性能测试工具