相关链接:
1. collections.namedtuple, typing.NamedTuple, @dataclasses.dataclass概述
2. collections.namedtuple详解
3. typing.NamedTuple详解
4. @dataclasses.dataclass详解
@dataclasses.dataclass
通过自动生成方法和类型注解支持,显著简化数据类的定义。其灵活性(如字段定制、可变性控制)和与静态检查工具的兼容性,使其成为替代传统类、字典或元组的理想选择。适用于需要结构化数据且兼顾开发效率的场景,尤其适合中大型项目中的核心数据建模。
一、@dataclasses.dataclass 核心功能
1. 自动生成特殊方法
- 自动添加
__init__
、__repr__
、__eq__
等方法,减少样板代码。 - 示例:
@dataclass
class Point:
x: int
y: int
等价于手动编写构造函数和字符串表示方法。
2. 类型注解支持
- 强制声明字段类型(如
name: str
),支持静态类型检查工具(如mypy
)。 - 支持嵌套类型(如
List[Dict]
)和Optional
注解。
3. 可变性控制
- 默认字段可修改,设置
frozen=True
使实例不可变(类似NamedTuple
)。
@dataclass(frozen=True)
class Config:
timeout: int
二、装饰器接受的关键字参数
参数 | 作用 | 默认值 | 备注 |
---|---|---|---|
init | 作用是生成__init__ | True | 如果用户自己实现了__init__ 可以忽略该参数 |
repr | 作用是生成__repr__ | True | 如果用户自己实现了__repr__ 可以忽略该参数 |
eq | 作用是生成__eq__ | True | 如果用户自己实现了__eq__ 可以忽略该参数 |
order | 作用是生成__lt__ 、__le__ 、__gt__ 、__ge__ | False | 设为true时,需要自行定义用于比较的方法,否则抛出异常 |
unsafe_hash | 作用是生成__init__ | False | 语义复杂,不推荐使用 |
frozen | 防止意外更改实例 | False | 防止意外更改实例,相对安全,但不是绝对不可变 |
三、字段定制与高级配置
1. field()
函数
- 接受的关键字参数:
参数 | 作用 | 默认值 |
---|---|---|
default | 字段的默认值 | _MISSING_TYPE1 |
default_factory | 不接受参数的函数,用于产生默认值 | _MISSING_TYPE |
init | 把字段作为参数传给__init__方法 | True |
repr | 在__repr__方法中使用字段 | True |
compare | 在__eq__、__lt__等比较方法中使用字段 | True |
hash | 在__hash__方法中使用字段计算哈希值 | None2 |
metadata | 用户定义的数据映射;@dataclass忽略该参数 | None |
- 示例:
from dataclasses import dataclass, field
@dataclass
class User:
name: str
tags: list[str] = field(default_factory=list) # 动态生成默认值
id: int = field(init=False) # 不包含在构造函数中
2. 默认值与动态赋值
- 默认值需通过
default
或default_factory
设置:
@dataclass
class Data:
values: list = field(default_factory=lambda: [1, 2, 3])
四、高级用法
1. 继承与扩展
- 支持类继承,可结合
__post_init__
添加初始化后逻辑:
@dataclass
class Person:
name: str
age: int
def __post_init__(self):
if self.age < 0:
raise ValueError("Age cannot be negative")
2. 序列化与转换
- 通过
dataclasses.asdict()
和dataclasses.astuple()
转为字典/元组:
p = Person("Alice", 30)
print(asdict(p)) # 输出:{'name': 'Alice', 'age': 30}
五、特殊的伪类型
1. typing.ClassVar
- 核心作用:标注类变量(Class Variable),表示该变量属于类级别而非实例级别。类型检查器会识别此注解,避免将类变量误判为实例变量。
- 语法示例:
from typing import ClassVar
class MyClass:
class_var: ClassVar[int] = 10 # 类变量,所有实例共享
instance_data: list # 实例变量,每个实例独立
#类变量通过类名访问
print(MyClass.class_var) # 输出: 10
- 特点:
- 类变量通过类名直接访问(如
MyClass.class_var
),所有实例共享同一份数据。 - 若通过实例修改
ClassVar
变量,实际会创建同名实例属性,而非修改类变量。
- 类变量通过类名直接访问(如
- 注意:
- 使用
ClassVar
时,需通过类名而非实例访问变量。 - 如果尝试通过实例修改
ClassVar
,实际会创建一个同名的实例属性,不会影响类变量本身。
- 使用
2. typing.InitVar
- 核心作用:在数据类(
dataclass
)中标记初始化参数,表示该参数仅在__init__
方法中使用,不作为实例属性保留。通常结合__post_init__
方法处理逻辑。 - 语法示例:
from dataclasses import dataclass, InitVar
@dataclass
class Person:
name: str
age: InitVar[int] # 仅用于初始化,不保留为实例属性
adult: bool = False
def __post_init__(self, age: int):
self.adult = age >= 18
p = Person("Alice", 20)
print(p.adult) # 输出: True
print(hasattr(p, "age")) # 输出: False (age 不是实例属性)
- 特点:
InitVar
字段会被包含在生成的__init__
方法参数中,但不会生成对应的实例属性。- 需在
__post_init__
方法中显式处理InitVar
参数(如计算或验证逻辑)。
- 注意:
InitVar
标注的参数需在__post_init__
方法中显式处理。- 未标注为
InitVar
的参数默认会成为实例属性。
五、适用场景
1. 数据模型类
- 替代手动定义
__init__
和__repr__
的类(如 ORM 实体、配置类)。
2. 复杂数据结构
- 需动态默认值、继承或自定义初始化逻辑的场景。
3. 需要类型安全的可变数据
- 结合类型注解和 IDE 提示提升代码可维护性。
<< 上一篇
3. typing.NamedTuple详解