元类(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元类的实例,我们可以用下面的图来表示这种关系。
创建类: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