元类(metaclass)是 Python 中一个高级概念,它控制了类的创建过程。在 Python 的开源框架中,可以看到很多元类的相关身影,学习并理解元类也可以帮助理解 Python 中的类的创建过程。

一切皆对象

在 Python 中一切皆对象,类也不例外,类也是一个对象,可以像普通对象一样进行赋值、拷贝以及作为函数的参数使用。我们知道每个对象都有一个类型,在 Python 中可以通过 type() 函数进行查看,一个 Python 实例它的类型是 class 类名,那类这个对象的类型又是什么呢,可以通过下面的一组例子进行查看。

class Foo:
    def __init__(self):
        pass

foo = Foo()
# 也可以通过 obj.__class__ 查看类型
print(type(foo))   # <class '__main__.Foo'>
print(type(Foo))   # <class 'type'>

print(type(1))     # <class 'int'>
print(type(int))   # <class 'type'>
print(type(type))  # <class 'type'>

我们可以看到实例 foo 的类型是class Foo, 而 Foo (类本身) 的类型是 type ,更近一步地,查看内置类型以及 type 本身的类型。我们可以发现,类的类型是 type,这实际上就是元类。正如普通对象是类的实例一样,Python 中的任何类都是type元类的实例,我们可以用下面的图来表示这种关系。

图1. Python 类型示意图

创建类:type

类也是对象,可以在运行时动态的创建它们,就像其他任何实例对象一样,例如可以在函数中创建类。另外,借助type,也能动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。可以像这样使用:type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值)),使用这种方式上一节中的类可以使用下面的方式创建:

Foo = type('Foo', (), {})
print(type(Foo))  #<class 'type'>

在 Python 中类也是对象,事实上,当我们使用关键字 class 创建 Python 类时,在幕后就是通过 type 元类实现的。

元类:创建类的类

有了上面的理解之后,我们知道元类就是用来创建类的类。创建类是为了创建实例对象,而创建元类就是为了创建类,我们可以把元类视为是 “类工厂”。type 是 Python 内建的元类,同样我们也可以在 Python 中创建自己的元类。

在 Python 中我们可以为一个类添加 __metaclass__ 属性,这样 Python 就会使用元类来创建类。

class Foo(object):
    __metaclass__ = something
    ...

在这段代码中,Python 遵循这样的流程来创建类:

  • 创建Foo,查看其是否有 __metaclass__这个属性:

    a. 如果有,用 __metaclass__ 创建;

    b. 如果没有,依次在父类、模块中寻找是否有这个属性,如果有就用其中 __metaclass__ 创建;

    c. 如果上面的过程都没有找到,那么就用内置的 type 来创建这个类对象。

这里的 __metaclass__ 中就是用来可以创建一个类的代码,或者其他使用到 type 以及子类化 type 的代码。

实际实现中,我们可以通过继承 type 来实现一个元类,在类定义时通过指定 metaclass 来使用定义的元类。

# 定义元类
class MyMeta(type):
    def __new__(cls, name, bases, dct):
        print(f"Creating class {name} with bases {bases} and attributes {dct}")
        return super(MyMeta, cls).__new__(cls, name, bases, dct)


# 指定 metaclass 使用元类
class MyClass(metaclass=MyMeta):
    def __init__(self, x):
        self.x = x


# 创建一个 MyClass 实例
obj = MyClass(10)

## Creating class MyClass with bases () and attributes {'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x10386c9d0>}

例如通过元类,我们可以很简单的实现一个单例模式:

class Singleton(type):
    """单例元类."""
    def __init__(cls, *args, **kwargs):
        cls._instance = None
        super(Singleton, cls).__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instance


class DemoClass(metaclass=Singleton):
    def __init__(self):
        pass


obj1 = DemoClass()
obj2 = DemoClass()

print(id(obj1) == id(obj2))  # True

参考