使用元类进行 Python 元编程

2025年3月17日 | 阅读 8 分钟

元编程(Metaprogramming)听起来可能很新,但如果用户曾经使用过装饰器(Decorators)或元类(metaclasses),那么他们就已经在项目中使用了元编程。因此,我们可以说元编程就是用于操作程序的程序。

在本教程中,我们将讨论元类及其用途,以及它们的替代方案。由于这是 Python 的一个高级主题,建议用户在开始本教程之前复习一下“Python 装饰器”和“Python OOPS 概念”的基础知识。

元类(Metaclasses)

在 Python 中,每个模块或函数都与某种类型相关联。例如,如果用户有一个整数值的变量,那么它就与“int”类型相关联。用户可以通过 type() 方法了解任何事物的类型。

示例

输出

Type of number is: <class 'int'>
Type of list is: <class 'list'>
Type of name is: <class 'str'>

解释 -

Python 中的每种类型都是由类定义的。因此,在上面的例子中,它们是 'int'、'list' 或 'str' 类的对象,这与 C 语言或 Java 语言不同,在这些语言中,int、char 和 float 是基本数据类型。用户可以通过创建该类型的类来创建新的类型。例如,我们可以通过 City 类创建一个新的 Object 类型。

示例

输出

Type of City_object is: <class '__main__.City'>

Python 中的类也是一个对象,因此,像其他对象一样,它是元类(Metaclass)的实例。元类是一种特殊的类类型,它负责创建类和类对象。所以,例如,如果用户想找到“City”类的类型,他们会发现它是“type”。

示例

输出

Type of City class is: <class 'type'>

由于类也是对象,我们可以修改它们。例如,我们可以像处理其他对象一样,向类中添加或删除字段或函数。

示例

输出

65
Pune

我们可以总结元类如下:

元类用于创建类,而这些类可以创建对象。

Metaprogramming with Metaclasses in Python

元类负责创建类,因此用户可以通过插入代码或额外的操作来修改类的创建方式,从而编写自己的自定义元类。通常情况下,用户不需要自定义元类,但在特殊情况下是必要的。

有些问题可以使用元类或非元类来解决,但有些情况只有元类才能解决。

如何创建自定义元类

要创建自定义元类,用户自定义元类必须继承 type 元类,通常会重写,例如:

  • __new__(): __new__() 函数在 __int__() 函数之前执行。它用于创建对象并返回。用户可以通过重写此函数来控制对象的创建方式。
  • __int__(): __int__() 函数用于初始化已创建的对象,该对象作为参数传递。

用户可以直接使用 type() 方法创建类。type() 方法的调用方式如下:

  1. 如前例所示,用户可以传入一个参数调用它,它将返回类型。
  2. 用户可以传入三个参数调用它。它将创建一个类。传递给它的参数如下:
    • 类的名称
    • 传入一个包含类继承的基类的元组
    • 类字典:这将作为类的本地命名空间,其中填充了函数和变量。

示例

输出

The Type of City class:  <class 'type'>
 The Type of City_object:  <class '__main__.City'>
This is a inherited method!
This is City class method!
Mark Jackson

现在,让我们看看如何在不直接使用 type() 函数的情况下创建元类。例如,我们将创建一个名为 MultiBases 的元类,它将检查正在创建的类是否继承自多个基类。如果是,它将引发一个错误。

示例

输出

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-409c90c285d5> in <module>
     29     pass
     30 # This will raise an error!
---> 31 class S(P, Q, R):
     32     pass

<ipython-input-2-409c90c285d5> in __new__(city, city_name, bases, citydict)
      6         # it will raise an error
      7         if len(bases)>1:
----> 8             raise TypeError("There are inherited multiple base classes!!!")
      9 
     10         # else it will execute the __new__() function of super class, that is

TypeError: There are inherited multiple base classes!!!

如何用元类解决问题

有些问题用户可以通过使用元类和装饰器来解决。但有些问题只能通过使用元类来找到解决方案。例如,用户想要调试类函数,他们希望的是,每当类函数被执行时,都会在执行其主体之前打印其完整的限定名称。

示例

输出

The full name of this method: Calculator.add
13
The full name of this method: Calculator.mul
42
The full name of this method: Calculator.div
3.0

解释 -

上述解决方案工作正常,但有一个问题,即用户希望将装饰器方法应用于继承了“Calculator”类的所有子类。在这种情况下,用户必须单独将装饰器方法应用于每个子类,就像我们在上面的 Calculator 类示例中所做的那样。

现在,真正的问题是,类可能有很多子类,单独应用装饰器方法到每个子类既困难又耗时。因此,为了解决这个问题,用户必须确保每个子类都具有此调试属性,他们应该寻找基于元类的解决方案。

示例

我们将正常创建类,然后立即使用 debug 方法装饰器进行包装。

输出

The full name of this Function: Calculator.add
10
The full name of this Function: Calculator_adv.mult
21

用户何时应该使用元类

用户不经常使用元类,因为元类主要用于复杂的情况。但有少数情况用户可以使用元类。

  • 如上面的例子所示,元类用于向下生成继承层次结构。这也会影响所有子类。如果用户有这种情况,他们可以使用元类。
  • 如果用户想自动更改类,他们可以在类创建时使用元类。
  • 如果用户是应用程序编程接口(API)开发者,他们可以为此目的使用元类。

结论

在本教程中,我们讨论了元类、如何自定义元类,以及用户如何使用它们来解决复杂编程问题和它们的替代方案。


下一个主题Python 精度处理