Python中的__init_subclass__

2025年3月5日 | 阅读7分钟

理解 Python 中的类

类被定义为实例变量的存储,类是用于指定对象类型的。类用于创建尽可能多的对象实例。对象是具有某些属性(也称为数据成员)和行为(也称为成员函数)的已识别实体。具有相同特征和同一类的行为的一组对象。类是用户定义的原型,通过类来创建对象。要创建新对象,请定义一个新类,该类能够创建类型的实例,每个类都关联有处理其状态的属性。考虑这个例子:你需要管理很多狗,每只狗的品种和年龄都可能不同。因此,在使用列表记录这些信息时,第一个元素可以是品种,第二个可以是年龄,依此类推;但是当处理 100 只不同的狗时,很难确定哪个元素与哪个属性相关联。然而,你也可以包含程序能够识别的每只狗的其他属性。这种方法相当混乱,而这正是类有用武之地的地方,即使只是为了组织目标。

创建类的语法

要定义类,需要使用 class 关键字

定义对象的语法

类可以被定义为一种用户创建的数据结构,它有自己的数据以及操作这些数据的函数。这些可以通过创建类的实例来访问和利用。

与 Python 类相关的要点

  • Class 关键字在 Python 中声明类。
  • 属性是属于类的变量。
  • 属性始终是公开访问的。

在 Python 中创建类

代码

使用 class 关键字定义类,类名是 Car。

Python 类的对象

对象是类的实例,类用作蓝图。

对象包含

  • 状态:定义属性。
  • 行为:确定它如何响应其他对象。
  • 身份:允许与其他对象交互。

声明类的对象

当从类创建实例对象时,在这种情况下,被实例化的类称为已实例化。类的所有对象都具有相似的特征,类的每个对象行为相似,但这些特征的值在不同的实例中可能不相同。类的实例属于特定类,一个类可以包含多个实例。

Python 类和对象的示例

要在像 Python 这样的面向对象编程语言中创建一个对象,首先要创建一个类,然后创建一个对象。这个过程被称为对象实例化。

代码

输出

 
vehicle 
I'm a vehicle 
I'm a car   

说明

在名为 Car 的类中,我们创建了一个名为 'sports_car' 的对象,它实际上是一辆车。正如我们所见,这个类有两个类属性,表明 sports_car 是一种交通工具,更具体地说,是一辆汽车。该类有两个属性:type 1 和 type 2。名为 display_info() 的方法被定义为显示 "I'm a {type1}" 和 "I'm a {type2}"。创建了 Car 类的对象,并显示了创建对象的 type1 字段。最后但同样重要的是,最后一个方法是 display_info() 方法。

Self 参数

通过语法 object.method(param1, param2) 在对象上调用方法,该语法在内部转换为 class.method(object, param1, param2)。

代码

输出

 
Hello, my name is Alice, and I work at TechCorp.   

在上面的代码中,创建了一个名为 python 的类,其构造函数是 name 和 company,show 方法显示一个人的姓名和公司。创建了 Python 类的对象,并调用 show 方法打印信息。

Python 3 中包含了 __init_subclass__ 方法。它是一个特殊方法,允许在实例化子类时进行扩展。在引入新子类时,它作为一个钩子在某个阶段触发,允许程序员调整子类的行为。因此,可以维护包含大量类的大型代码库。

让我们看一个 __init_subclass__ 的例子。为了清楚地理解 __init_subclass__,请考虑 Animal 作为基类,它有许多子类,如 Cat、Dog 和 Fish。因此,我们可以得出结论,每当创建一个新的 Car 子类时,就会关联一个特定的属性,即 model。创建新的 __init_subclass__ 所遵循的步骤。

  • Animal 类被定义为基类,并带有 __init_subclass__ 方法。
  • 检查条件,看子类中是否存在属性 species。
  • 如果属性缺失,则会引发 TypeError。
  • 子类被定义为 cat、Dog 和 Fish。

代码

输出

 
ERROR!
Traceback (most recent call last):
  File "<main.py>", line 13, in <module>
  File "<main.py>", line 5, in __init_subclass__
TypeError: Truck must have a model attribute   

说明

在上面的代码中,基类是 Vehicle,子类是 Sedan、SUV 和 Truck。Truck 类不包含 model 属性,这会引发 TypeError。

子类的高级用例

注册子类

当需要创建子类注册表时,__init_subclass__ 特别有用。这个子类注册表有助于在不同程序之间进行对象的序列化和反序列化。假设以下示例。

  • 在基类中创建了一个带有空字典的插件,并命名为 _registry,这是一个私有变量。
  • 在 Plugin 类中实现了 __init_subclass__。
  • Module A 和 Module B 用于定义子类。
  • 打印子类的 _registry 以查看已注册的子类。

代码

输出

 
{'ModuleA': <class '__main__.ModuleA'>, 'ModuleB': <class '__main__.ModuleB'>}   

说明

在上面的代码中,创建了一个插件类,并在类中创建了一个私有变量来注册所有子类,这个注册表有助于对象的序列化和反序列化。

强制执行类属性

__init_subclass__ 方法用于使某些属性在子类中成为强制性的,换句话说,是为了确保子类包含某些属性。此功能有助于组织代码,同时对代码组织施加一些符合某些设计模式的约束。让我们看一个例子

  • 定义一个名为 Transport 的类作为基类,其中包含 __init_subclass__ 方法。
  • 列出了所需的属性,如 fuel_type 和 number_of_wheels,列出了 wheels。
  • 对所需的属性进行迭代,以检查子类中是否存在。
  • 如果缺少所需的属性,则会引发 AttributeError。

代码

输出

 
ERROR!
Traceback (most recent call last):
  File "<main.py>", line 14, in <module>
  File "<main.py>", line 8, in __init_subclass__
AttributeError: Scooter must have a 'number_of_wheels' attribute   

说明

对于对编码所做的修改,Transport 类已被声明为具有强制参数 fuel_type 和 number_of_wheels。创建了两个子类 Sedan 和 Scooter。如果子类中缺少任何必需的属性,则会引发 AttributeError。例如,在 Scooter 类中,没有 number_of_wheels 属性;因此,Transport 类会引发一个异常。

结论

__init_subclass__ 是 Python 方法,它是一个强大的功能,允许程序员自定义子类的创建。