Python 数据类

PythonBeginner
立即练习

介绍

在这个 Lab 中,你将接触到 Python Dataclasses(数据类),这是从 Python 3.7 开始提供的一个强大特性。数据类是普通的 Python 类,主要用于存储数据。它们使用一个装饰器 @dataclass 来自动生成特殊方法,如 __init__() 和 __repr__(),这极大地减少了样板代码(boilerplate code)。

在整个 Lab 过程中,你将学习如何:

  • 定义一个简单的数据类。
  • 将其与标准类的实现进行比较。
  • 为字段设置默认值。
  • 处理类型提示(type hints),包括使用 typing.Any。

完成本 Lab 后,你将能够用更简洁、更清晰的方式在 Python 中编写以数据为中心的类。

数据类特性

在这一步,你将学习数据类的基本特性。我们将从一个标准的 Python 类开始,该类用于存储数据,然后看看数据类是如何简化它的。

一个标准的类要求你手动编写 __init__ 方法来初始化属性。

例如,一个表示数字的类看起来像这样:

class Number:
    def __init__(self, val):
        self.val = val

obj = Number(2)
print(obj.val)

使用 @dataclass 装饰器后,Python 会自动生成 __init__ 方法,以及其他有用的方法,例如用于清晰字符串表示的 __repr__ 方法。主要要求是你必须为字段提供类型提示(type hints)。

注意: 如果你不熟悉使用 WebIDE(如 VS Code),请先在 这个实验 中学习。

让我们使用 @dataclass 装饰器创建相同的 Number 类。

从左侧的文件浏览器中打开 features.py 文件。向其中添加以下代码:

from dataclasses import dataclass

@dataclass  ## 此装饰器将类转换为数据类
class Number:
    val: int  ## 需要类型提示

## __init__ 方法是自动创建的
obj = Number(2)

## 打印 'val' 属性的值
print(obj.val)

## __repr__ 方法也是自动创建的,提供了很好的表示形式
print(obj)

现在,从终端运行脚本以查看输出。

python3 features.py

你将看到以下输出。第一行是属性的值,第二行是自动生成的 __repr__ 方法提供的清晰的对象表示。

2
Number(val=2)

正如你所见,数据类版本更简洁,需要的样板代码更少。

使用默认值

在这一步,你将探索如何在数据类的字段中添加默认值。当某些属性是可选的时,这是一个常见需求。

需要记住的一个关键规则是:任何带有默认值的字段必须声明在所有没有默认值的字段之后。

让我们创建一个 Product 数据类,其中 name 是必需字段,而 count 和 price 是可选的,并带有默认值。

打开文件浏览器中的 defaults.py 文件。添加以下代码:

from dataclasses import dataclass

@dataclass
class Product:
    name: str        ## 必需字段
    count: int = 0   ## 带有默认值的可选字段
    price: float = 0.0  ## 带有默认值的可选字段

## 仅提供必需的 'name' 字段来创建一个实例
obj = Product("Python")

## 打印属性以查看默认值生效情况
print(f"Name: {obj.name}")
print(f"Count: {obj.count}")
print(f"Price: {obj.price}")

现在,在终端中执行脚本。

python3 defaults.py

输出将显示 count 和 price 已自动分配了它们的默认值。

Name: Python
Count: 0
Price: 0.0

此特性使得创建带有可选属性的对象非常方便。

处理类型提示

在这一步,你将理解在数据类中使用类型提示(type hints)的要求。如你所见,定义数据类时,为每个字段提供类型是强制性的。

然而,有时你可能不想或不能为某个字段指定具体的(concrete)数据类型。对于这些情况,Python 的 typing 模块提供了一个名为 Any 的特殊类型。使用 Any 告诉类型检查器(type checker)该值可以是任何类型。

让我们创建一个数据类,其中我们对字段使用 Any。

打开文件浏览器中的 type_hints.py 文件,并添加以下代码:

from dataclasses import dataclass
from typing import Any

@dataclass
class WithoutExplicitTypes:
   name: Any
   value: Any = 42

## 创建一个实例,其中 'name' 是字符串。'value' 使用其默认值。
obj1 = WithoutExplicitTypes(name="Test")
print(f"Object 1: name={obj1.name}, value={obj1.value}")

## 创建另一个实例,其中 'name' 是整数,'value' 是字符串。
obj2 = WithoutExplicitTypes(name=123, value="hello")
print(f"Object 2: name={obj2.name}, value={obj2.value}")

现在,从终端运行脚本。

python3 type_hints.py

你将看到以下输出,证明这些字段可以容纳不同类型的值。

Object 1: name=Test, value=42
Object 2: name=123, value=hello

虽然 Any 提供了灵活性,但通常来说,只要有可能,使用特定类型是个好习惯,这样可以让你的代码更健壮且具有自文档化(self-documenting)的特性。

总结

在这个实验(Lab)中,你学习了如何使用 Python 的数据类(dataclasses)来创建简洁且易读的数据中心类。

你涵盖了以下关键概念:

  • @dataclass 装饰器会自动生成特殊方法,如 __init__() 和 __repr__(),从而减少了样板代码(boilerplate code)。
  • 如何为字段定义默认值,以及默认值必须跟在没有默认值的字段之后的规则。
  • 数据类中类型提示的强制性,以及如何在不希望指定特定类型时使用 typing.Any。

数据类是现代 Python 中编写干净、可维护和高效代码的强大工具,尤其是在处理主要存储状态的类时。