如何在dataclass中添加无类型注解字段?类型任意时如何省略注解?
嘿,这两个关于dataclass字段的问题我很熟,给你详细解答下!
问题1:如何在dataclass中添加无需类型注解的字段?
首先得明确一个核心规则:Python的dataclass默认只会把带有类型注解的变量当作数据类字段。如果变量没有加类型注解,它会被当成普通的类属性——不会出现在__dataclass_fields__里,也不会被自动包含在__init__、__repr__这些dataclass自动生成的方法中,就像你给出的示例那样:
from dataclasses import dataclass @dataclass class Favs: fav_number: int = 80085 fav_duck = object() # 无类型注解,属于普通类属性 fav_word: str = 'potato'
运行验证一下就能看出来区别:
>>> Favs() Favs(fav_number=80085, fav_word='potato') # fav_duck不在实例的repr里 >>> print(*Favs.__dataclass_fields__) fav_number fav_word # 只有带注解的变量才被识别为dataclass字段 >>> Favs.fav_duck <object object at 0x7fffea519850> # 它是类层面的属性,不是实例字段
如果你想让无注解的变量也成为dataclass字段,可以手动给类的__annotations__字典添加字段名(值设为typing.Any),再配合dataclasses.field设置默认值:
from dataclasses import dataclass, field from typing import Any @dataclass class Favs: fav_number: int = 80085 fav_word: str = 'potato' # 手动把fav_duck注册为dataclass字段 Favs.__annotations__['fav_duck'] = Any Favs.fav_duck = field(default=object())
再验证就会发现它已经是正规的dataclass字段了:
>>> Favs() Favs(fav_number=80085, fav_word='potato', fav_duck=<object object at 0x7fffea519870>) >>> print(*Favs.__dataclass_fields__) fav_number fav_word fav_duck
不过这种方式有点繁琐,通常更推荐用下面问题2里提到的Any注解方案。
问题2:当dataclass中的某个字段可以是任意类型时,如何省略其类型注解?
严格来说,dataclass要求字段必须有类型注解才能被识别为数据字段。但如果你的需求是让字段支持任意类型,同时不想写具体的类型限制,最简洁的方式就是用typing.Any作为类型注解——它表示该字段可以接受任何类型的值,相当于“无限制”的注解,写法也很简单:
from dataclasses import dataclass from typing import Any @dataclass class Favs: fav_number: int = 80085 fav_duck: Any = object() # 用Any表示任意类型,等价于放开类型限制 fav_word: str = 'potato'
这样写的话,fav_duck会被正常识别为dataclass字段,而且可以赋值任意类型:
>>> Favs() Favs(fav_number=80085, fav_duck=<object object at 0x7fffea519890>, fav_word='potato') >>> print(*Favs.__dataclass_fields__) fav_number fav_duck fav_word # 随便赋值什么类型都可以 >>> Favs(fav_duck="我是字符串!") Favs(fav_number=80085, fav_duck="我是字符串!", fav_word='potato') >>> Favs(fav_duck=12345) Favs(fav_number=80085, fav_duck=12345, fav_word='potato')
如果你真的完全不想写任何类型注解(哪怕是Any),也可以通过__post_init__来初始化字段,但这种方式下字段不会被包含在__init__参数里,也不会被__dataclass_fields__识别,实用性不强:
from dataclasses import dataclass, MISSING @dataclass class Favs: fav_number: int = 80085 fav_word: str = 'potato' def __post_init__(self): # 初始化任意类型的字段 if not hasattr(self, 'fav_duck'): self.fav_duck = object()
所以综合来看,用Any作为类型注解是最优解——既满足了“任意类型”的需求,又能让字段被dataclass正常管理。
内容的提问来源于stack exchange,提问作者wim

