本文概述
Python中的元类是定义类行为方式的类的类。类本身就是元类的实例。 Python中的类定义了该类实例的行为。为了更好地理解元类, 需要具有使用Python类的先验经验。在深入研究元类之前, 让我们先了解一些概念。
Python中的一切都是对象
class TestClass():
pass
my_test_class = TestClass()
print(my_test_class)
<__main__.TestClass object at 0x7f6fcc6bf908>
可以动态创建Python类
Python中的type使我们能够找到对象的类型。我们可以继续检查上面创建的对象的类型。
type(TestClass)
type
type(type)
type
等等, 发生了什么事?我们希望上面创建的对象的类型为class, 但事实并非如此。坚持那个想法。我们将在后面进一步介绍。我们还注意到类型本身的类型是type。它是类型的实例。该类型所做的另一个神奇的事情是使我们能够动态创建类。让我们在下面展示我们将如何做。如下所示, 将使用类型创建以下所示的srcmini类:
class srcmini():
pass
srcminiClass = type('srcmini', (), {})
print(srcminiClass)
print(srcmini())
<class '__main__.srcmini'>
<__main__.srcmini object at 0x7f6fcc66e358>
在上面的示例中, srcmini是类名, 而srcminiClass是保存类引用的变量。使用type时, 我们可以使用字典传递类的属性, 如下所示:
PythonClass = type('PythonClass', (), {'start_date': 'August 2018', 'instructor': 'John Doe'} )
print(PythonClass.start_date, PythonClass.instructor)
print(PythonClass)
August 2018 John Doe
<class '__main__.PythonClass'>
如果我们希望PythonClass继承自srcmini类, 则在使用type定义类时将其传递给第二个参数
PythonClass = type('PythonClass', (srcmini, ), {'start_date': 'August 2018', 'instructor': 'John Doe'} )
print(PythonClass)
<class '__main__.PythonClass'>
现在, 这两个概念已不复存在, 我们意识到Python使用元类创建了类。我们已经看到Python中的所有东西都是一个对象, 这些对象是由元类创建的。每当我们调用class创建一个class时, 就有一个metaclass可以在后台创建class的魔力。在上面的实践中, 我们已经看到类型可以做到这一点。它类似于创建字符串的str和创建整数的int。在Python中, ___class__attribute使我们能够检查当前实例的类型。让我们在下面创建一个字符串并检查其类型。
article = 'metaclasses'
article.__class__
str
我们也可以使用type(article)检查类型。
type(article)
str
当检查str的类型时, 我们还发现它是类型。
type(str)
type
当我们检查float, int, list, tuple和dict的类型时, 我们将得到类似的输出。这是因为所有这些对象都是type类型。
print(type(list), type(float), type(dict), type(tuple))
<class 'type'> <class 'type'> <class 'type'> <class 'type'>
我们已经看到类型创建类。因此, 当我们检查__class__的__class__时, 它应该返回类型。
article.__class__.__class__
type
创建自定义元类
在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()的实例, 则它将直接用作元类。
__new__和__init__
也可以通过以下两种方式之一定义元类。我们将在下面解释它们之间的区别。
class MetaOne(type):
def __new__(cls, name, bases, dict):
pass
class MetaTwo(type):
def __init__(self, name, bases, dict):
pass
__new__用于在创建类之前定义字典或基元组的情况。 __new__的返回值通常是cls的实例。 __new__允许不可变类型的子类自定义实例创建。可以在自定义元类中覆盖它以自定义类的创建。通常在创建对象之后调用__init__以便对其进行初始化。
元类__call__方法
根据官方文档, 我们还可以通过在元类中定义一个自定义__call __()方法来覆盖其他类方法, 该方法在调用该类时允许自定义行为。
元类__prepare__方法
根据Python的数据模型文档
一旦标识了适当的元类, 就准备好类名称空间。如果元类具有__prepare__属性, 则将其称为命名空间= metaclass .__ prepare __(名称, 基数, ** kwds)(其中其他关键字参数(如果有)来自类定义)。如果元类没有__prepare__attribute, 则将类名称空间初始化为空的有序映射。 -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%的用户应该担心的更深的魔力。如果你想知道是否需要它们, 则不需要(实际上需要它们的人可以肯定地知道他们需要它们, 并且不需要任何解释。为什么)。” -真正的python
如果你想了解有关Python的更多信息, 请启动srcmini的Python编程技能专区。
参考文献
链接一
链接二
评论前必须登录!
注册