dataclasses模块的三个重要函数:

  1. dataclass:类的装饰器函数,自动生成数据类的各种魔术方法
  2. field:用于精细控制每个字段的行为
  3. fields:用于获取类的字段

后边写数据类的时候用一用这种写法,看看和传统写法相比到底能不能让结构更清晰,更省代码量,一个例子:

利用dataclasses模块提供的函数创建纯数据类
from dataclasses import dataclass, field, fields from typing import List @dataclass class Config: api_key: str timeout: int = field(default=30, metadata={"unit": "seconds"}) retry_count: int = 3 endpoints: List[str] = field(default_factory=list) config = Config(api_key="abc123") print(fields(config))

设置属性默认值的坑

Python 语言的基本规则:函数的默认参数在定义时(而非调用时)被求值。对于可变参数例如列表直接设置属性为[]会使得生成的所有实例的对应属性是同一个[]

from dataclasses import dataclass

@dataclass
class Wrong:
    name: str
    items: list = []  # 默认值是 [] 字面量

# 创建两个实例
w1 = Wrong("张三")
w2 = Wrong("李四")

# 只给 w1 添加元素
w1.items.append(1)

print(w1.items)  # 输出:[1]
print(w2.items)  # 输出:[1]  ← 惊不惊喜?李四的列表也被改了!

在普通函数/类中,标准解法是使用 None 做哨兵值,在函数体内部新建列表:

# 普通类/函数的标准安全写法
def add_item(item, lst=None):
    if lst is None:
        lst = []       # 每次调用都新建列表
    lst.append(item)
    return lst

print(add_item(1))  # 输出:[1]
print(add_item(2))  # 输出:[2]  ← 完美独立!

@dataclass 里的field(default_factory=list),本质上就是 Python 官方把这套 None + 判断的模板代码,封装成了更优雅、语义更清晰的写法。写成feild(default_factory=list)就OK了,它告诉解释器不要用现有的对象,请在__init__运行时执行 list() 工厂函数取返回值。

参考:https://docs.pythonlang.cn/3/library/dataclasses.html