个性化阅读
专注于IT技术分析

Python元类使用简介

本文概述

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编程技能专区。

参考文献

链接一

链接二

赞(0)
未经允许不得转载:srcmini » Python元类使用简介

评论 抢沙发

评论前必须登录!