飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

数据类 dataclass

从 Python 3.7 起,dataclasses 模块提供了一种简洁的方式来创建主要用于存储数据的类。数据类通过装饰器自动生成特殊方法,避免了手写大量样板代码。在 Python 3.12 中,数据类与类型注解体系结合得更加紧密。

基本用法

使用 @dataclass 装饰器,配合类型注解声明字段,解释器会自动生成 __init__、__repr__ 和 __eq__ 等方法:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
print(p)           # Point(x=1.0, y=2.0)
print(p.x)         # 1.0

对比传统类定义,数据类省去了手写 __init__ 和赋值语句的繁琐:

# 传统写法
class PointOld:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __repr__(self):
        return f"PointOld(x={self.x}, y={self.y})"
    def __eq__(self, other):
        if not isinstance(other, PointOld):
            return NotImplemented
        return self.x == other.x and self.y == other.y

字段默认值

数据类字段可以指定默认值,有默认值的字段必须放在无默认值字段之后:

from dataclasses import dataclass

@dataclass
class Config:
    name: str
    debug: bool = False
    timeout: int = 30

c = Config("server")
print(c)  # Config(name='server', debug=False, timeout=30)

如果默认值是可变对象(如列表或字典),必须使用 field(default_factory=...),否则所有实例会共享同一个可变对象:

from dataclasses import dataclass, field

@dataclass
class Item:
    name: str
    tags: list = field(default_factory=list)

a = Item("apple")
b = Item("banana")
a.tags.append("fruit")
print(b.tags)  # [],各自独立

直接使用 tags: list = [] 会导致所有实例共享同一个列表,这是与函数默认参数类似的陷阱。

field() 的更多选项

field() 函数可以精细控制字段行为:

from dataclasses import dataclass, field

@dataclass
class Product:
    name: str
    price: float = field(compare=False)  # 不参与比较
    quantity: int = field(default=0, repr=False)  # 不显示在 repr 中
    _internal_id: int = field(init=False, repr=False)  # 不由 __init__ 接收

    def __post_init__(self):
        self._internal_id = hash(self.name)

__post_init__ 方法在自动生成的 __init__ 执行完毕后被调用,适合进行字段校验或派生值计算。

自动生成的方法

@dataclass 的 eq、order、frozen 等参数控制自动生成哪些方法:

from dataclasses import dataclass

@dataclass(order=True)
class Student:
    name: str
    score: int

s1 = Student("Alice", 85)
s2 = Student("Bob", 92)
print(s1 < s2)   # True,按字段顺序比较(先比 name,再比 score)

启用 order=True 会自动生成 __lt__、__le__、__gt__、__ge__。比较按字段定义的顺序逐字段进行。如果只想按特定字段排序,可以配合 field(compare=False) 屏蔽无关字段。

frozen=True 创建不可变数据类,实例创建后任何修改都会触发 FrozenInstanceError:

from dataclasses import dataclass

@dataclass(frozen=True)
class ImmutablePoint:
    x: float
    y: float

p = ImmutablePoint(1.0, 2.0)
# p.x = 3.0  # dataclasses.FrozenInstanceError

不可变数据类的实例可以作为字典的键或放入集合中。

继承与数据类

数据类支持继承,子类可以添加新字段或覆盖父类字段的默认值:

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

@dataclass
class Employee(Person):
    employee_id: str
    department: str = "未分配"

e = Employee("张三", 30, "E001")
print(e)  # Employee(name='张三', age=30, employee_id='E001', department='未分配')

继承时,子类的字段排在父类字段之后,__init__ 的参数顺序也遵循这一规则。

转换为字典或元组

数据类实例可以方便地转换为字典或元组,便于序列化:

from dataclasses import dataclass, asdict, astuple

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
print(asdict(p))   # {'x': 1.0, 'y': 2.0}
print(astuple(p))  # (1.0, 2.0)

与 match-case 结合

Python 3.10+ 的 match 语句可以直接匹配数据类实例,按字段解构:

from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(0, 5)

match p:
    case Point(x=0, y=y):
        print(f"Y轴上的点,y={y}")
    case Point(x=x, y=0):
        print(f"X轴上的点,x={x}")
    case Point(x=x, y=y):
        print(f"普通点 ({x}, {y})")

与 typing.NamedTuple 和普通类的选择

方式可变自动生成方法适用场景
dataclass默认可变,frozen=True 可变__init__, __repr__, __eq__ 等大多数数据存储场景
typing.NamedTuple不可变继承自 tuple,有索引访问需要不可变且轻量
普通类完全自定义手动编写复杂行为,非纯数据

数据类在纯数据存储场景下是首选,它用最小的代码量提供了最大的便利性。

常见错误

忘记类型注解:

# 错误:没有类型注解,dataclass 不会将其识别为字段
@dataclass
class Bad:
    x = 0   # 这是类属性,不是数据类字段

# 正确
@dataclass
class Good:
    x: int = 0

可变默认值的陷阱:

from dataclasses import dataclass

# 错误:所有实例共享同一个列表
@dataclass
class BadItem:
    tags: list = []

# 正确:使用 default_factory
from dataclasses import field
@dataclass
class GoodItem:
    tags: list = field(default_factory=list)

有默认值的字段位置:

# 错误:有默认值的字段不能放在无默认值字段前面
# @dataclass
# class Bad:
#     x: int = 0
#     y: int      # TypeError

# 正确
@dataclass
class Good:
    y: int
    x: int = 0

数据类是 Python 3.7+ 中处理结构化数据的首选工具。它通过减少样板代码,让开发者专注于数据本身而非类的机械构造。

上一页
属性装饰器