整体说明
@dataclass是 Python 3.7 引入的一个装饰器,属于dataclasses模块,用于简化类的定义@dataclass在初始化对象后,还会自动生成常用方法如__init__、__repr__、__eq__等
@dataclass 用法示例
综合示例:汇总 @dataclass 各种用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69from dataclasses import dataclass, field
from typing import Union, Dict, List, Callable, Optional
from easydict import EasyDict as edict
# 下面的语句中的参数是可选的,常常用 @dataclass 即可
init=True, # 自动生成 __init__
repr=True, # 自动生成 __repr__
eq=True, # 自动生成 __eq__
order=True, # 自动生成比较方法(<、<=、>、>=)
unsafe_hash=False, # 不自动生成 __hash__
frozen=False # 允许属性修改, 若设置为 True 则不允许修改属性,报错dataclasses.FrozenInstanceError: cannot assign to field 'xx'
)
class DemoDataClass:
# 类型注解 + 必须参数(@dataclass注解过的类,字段的类型注解是必须需要的)
# 字段的类型注解仅做类型检查(如 mypy、Pyright)看的,实际上仍然可以赋值为任意类型
name: str
# 类型注解 + 默认值
age: int = 18
# 可能是 字典{str:edict} 或 列表[str],默认值取 None
tools: Union[Dict[str, edict], List[str]] = None
# 可以是 int,也可以是 None
children: Optional[int] = None
# 类型注解 + 默认工厂函数
tags: list = field(default_factory=lambda: ["python", "dataclass"])
# 不参与 __init__,表示该字段不会作为参数传递给构造函数,无法通过参数传入
# # 后续可在 __post_init__ 函数中自己定义或修改
# # 字段仍然是对象属性,可以被访问和修改,且仍然可以被 __repr__、__eq__ 等方法使用(除非通过 repr=False 或 compare=False 排除)
# # 通常用于那些需要在对象创建后由其他逻辑赋值的字段,比如自增主键、计算得出的值
id: int = field(init=False, default=0)
# 排除字段(不会参与比较和展示)
secret: str = field(default="hidden", repr=False, compare=False)
# 排除字段(不会参与比较和展示)
temp_data: dict = field(default_factory=dict, repr=False, compare=False)
# 函数字段定义
print_fn: Callable = lambda x: print(x)
# 用函数的方式定义字段,实际访问时必须按照字段语法访问
def age_name(self):
return "%s_%s" % (self.age, self.name) # 可使用类的字段访问
# 自定义初始化逻辑,在自动生成的 __init__ 函数执行后会被调用
def __post_init__(self):
self.id = hash(self.name + str(self.age)) # 仅初始化一次,以后再修改 name 或 age 不会引起 self.id 的变化(除非显示调用.__post_init__())
# 使用示例
demo1 = DemoDataClass(name="Alice")
# demo1 = DemoDataClass(name="Alice", id=100) # 报错:TypeError: DemoDataClass.__init__() got an unexpected keyword argument 'id'
demo2 = DemoDataClass(name="Bob", age=25, tags=["dev"], temp_data={"x": 1})
print(demo1) # DemoDataClass(name='Alice', age=18, tools=None, children=None, tags=['python', 'dataclass'], id=1490295676558033675, print_fn=<function DemoDataClass.<lambda> at 0x10e504a60>)
print(demo2) # DemoDataClass(name='Bob', age=25, tools=None, children=None, tags=['dev'], id=6412555795401615714, print_fn=<function DemoDataClass.<lambda> at 0x10e504a60>)
print(demo1 == demo2) # False;比较结果
print(demo1.tags) # ['python', 'dataclass'];默认工厂
print(demo1.secret) # hidden
print(demo1.age_name) # 18_Alice;须按照字段方式读取
# print(demo1.age_name()) # 报错 TypeError: 'str' object is not callable
demo1.tools = 100 # 请记住:字段类型仅作为声明检查,并不会直接报错
print(demo1) # DemoDataClass(name='Alice', age=18, tools=100, children=None, tags=['python', 'dataclass'], id=1490295676558033675, print_fn=<function DemoDataClass.<lambda> at 0x10e504a60>)
demo1.print_fn("print me") # print me示例说明
- 类型注解:所有字段都需要类型注解,
@dataclass会自动识别,没有给出类型的字段会报错 - 默认值:通过直接赋值或
field(default=...)指定 - 默认工厂:如
default_factory用于可变类型(如列表、字典) init=False:字段不参与初始化,只在类内部赋值,__init__内不可读repr=False, compare=False:字段不参与对象展示和比较__post_init__:用于自定义初始化逻辑order=True:自动生成排序方法frozen=True:设置后对象不可变(本例未启用)
- 类型注解:所有字段都需要类型注解,
附录:在 dataclass 中使用 field 和不使用 field 的区别
- 使用
field的字段- 通过
field()传递更多参数来控制字段行为 - 可以精细控制字段的初始化、展示、比较、哈希、默认值生成等行为,适合复杂需求
- 常用参数包括
default:指定默认值(同直接赋值)default_factory:指定一个工厂函数,用于生成默认值(常用于可变类型,如列表、字典)init:是否包含在__init__方法参数中(True/False)repr:是否包含在__repr__方法中(True/False)compare:是否用于比较(__eq__、__lt__等)hash:是否用于哈希metadata:可存储任意元数据
- 通过
- 不使用
field的字段- 直接用类型注解和可选的默认值定义字段
- 只能简单指定类型和默认值,适合简单场景
- 默认动作:
- 默认会参与
__init__方法(即可以通过构造函数传参) - 默认会参与
__repr__、__eq__、__hash__等自动生成的方法 - 默认不可用
default_factory,只能用普通的默认值(如字符串、数字、布尔等不可变类型)
- 默认会参与
- TLDR:
- 简单字段可直接定义,不用
field - 需要更细致控制(如可变类型、参与方法、元数据等)时,应使用
field()
- 简单字段可直接定义,不用