创建自定义元类
在 Python 中,我们可以在类定义中传入 metaclass 关键字来自定义类的创建过程。也可以通过继承已传入该关键字的类来实现。
class MyMeta(type):
pass
class MyClass(metaclass=MyMeta):
pass
class MySubclass(MyClass):
pass
如下所示,MyMeta 类的类型是 type,而 MyClass 和 MySubClass 的类型是 MyMeta。
print(type(MyMeta))
print(type(MyClass))
print(type(MySubclass))
<class 'type'>
<class '__main__.MyMeta'>
<class '__main__.MyMeta'>
当定义一个类而未指定元类时,将使用默认的 type 元类。如果给定了元类,且它不是 type() 的实例,那么它将被直接用作元类。
__new__ 和 __init__
元类也可以通过下面两种方式之一来定义。我们会在下文解释它们的区别。
class MetaOne(type):
def __new__(cls, name, bases, dict):
pass
class MetaTwo(type):
def __init__(self, name, bases, dict):
pass
__new__ 用于在类被创建之前设定 dict 或 bases 元组。__new__ 的返回值通常是 cls 的一个实例。__new__ 允许不可变类型的子类自定义实例创建。它可以在自定义元类中被重写,以定制类的创建过程。__init__ 通常在对象创建之后被调用,用于初始化对象。
元类的 __call__ 方法
根据官方文档,我们还可以在元类中定义自定义的 __call__() 方法,以在类被调用时实现自定义行为,从而覆盖其他类方法。
元类的 __prepare__ 方法
根据 Python 的数据模型文档
一旦确定了合适的元类,就会准备类的命名空间。如果元类具有 __prepare__ 属性,则按 namespace = metaclass.__prepare__(name, bases, **kwds) 调用(其中额外的关键字参数(如果有)来自类定义)。如果元类没有 __prepare__ 属性,则类命名空间会被初始化为一个空的有序映射。 - docs.python.org
使用元类实现单例设计
这是一种将类的实例化限制为唯一对象的设计模式。例如,在设计用于连接数据库的类时,这可能很有用。此时我们可能只需要连接类的一个实例。
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(SingletonMeta,cls).__call__(*args, **kwargs)
return cls._instances[cls]
class SingletonClass(metaclass=SingletonMeta):
pass
结论
在本文中,我们了解了什么是元类,以及如何在 Python 编程中实现它们。元类可用于日志记录、在创建时注册类、性能分析等场景。它们看起来相当抽象,您可能会想自己是否真的需要用到它们。长期的 Pythonista,Tim Peters 给出了最佳回答。
“元类是一种比 99% 的用户需要关心的更深层的魔法。如果您在想自己是否需要它们,那就不需要(真正需要的人会非常确定自己需要它们,也不需要解释原因)。” - realpython
如果您想进一步学习 Python,请开始 DataCamp 的 Python Programming 技能路径,并查看我们的 Python Classes 教程。
在Python中,元类是“类的类”,用于定义类的行为。类本身是元类的实例。Python 中的类定义了该类实例的行为。要充分理解元类,您需要具备使用 Python 类的经验。在深入探讨元类之前,我们先理清几个概念。
Python 中的一切都是对象
Python 类可以动态创建
Python 中的
type使我们能够获取对象的类型。我们可以继续检查上面创建的对象的类型。等等,发生了什么?我们可能会期望上面创建的对象类型是 class,但结果并不是。先记住这个问题,我们稍后会解释。我们还注意到
type本身的类型也是type。它是type的一个实例。type的另一个“魔法”是可以让我们动态创建类。下面展示如何做到这一点。下面的DataCamp类可以用type如下创建:在上面的示例中,
DataCamp是类名,而DataCampClass是保存该类引用的变量。使用type时,我们可以通过字典传入类的属性,如下所示:如果我们希望
PythonClass继承自DataCamp类,可以在使用type定义类时,将其作为第二个参数传入:理清这两个概念后,我们会意识到 Python 是通过元类创建类的。我们已经看到,Python 中的一切都是对象,而这些对象是由元类创建的。每当我们用
class创建一个类时,幕后的元类会负责完成类的创建。我们已在上文中看到type的实际表现。这类似于str创建字符串、int创建整数。在 Python 中,___class__属性可以让我们查看当前实例的类型。下面创建一个字符串并检查其类型。我们也可以使用
type(article)来检查类型。当我们检查
str的类型时,会发现它的类型是 type。当我们检查
float、int、list、tuple和dict的类型时,也会得到类似的输出。这是因为这些对象的类型都是type。我们已经看到
type会创建类。因此,当我们检查__class__的__class__时,它应返回type。