Python——@dataclass注解的使用


整体说明

  • @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
    69
    from dataclasses import dataclass, field
    from typing import Union, Dict, List, Callable, Optional
    from easydict import EasyDict as edict

    # 下面的语句中的参数是可选的,常常用 @dataclass 即可
    @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)

    # 用函数的方式定义字段,实际访问时必须按照字段语法访问
    @property
    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()