如何获取Python 3.7的新数据类字段类型?

84

Python 3.7 引入了一个名为数据类(data classes)的新特性。

from dataclasses import dataclass

@dataclass
class MyClass:
    id: int = 0
    name: str = ''

在函数参数中使用类型提示(注释)时,您可以使用inspect模块轻松获取注释的类型。如何获取dataclass字段的类型?


我宁愿不使用那样的私有字段。无论如何,谢谢。 - Kamyar
这不是一个私有字段。而且你刚刚接受了一个使用“dunder”属性的答案。 - jonrsharpe
顺便提一下,推荐使用 get_type_hints 而不是使用 __annotations__ - Andriy Ivaneyko
3个回答

119
检查__annotations__可以获得原始注释,但这些注释不一定对应于数据类的字段类型。像ClassVar和InitVar这样的东西出现在__annotations__中,即使它们不是字段,继承的字段也不会显示出来。
相反,调用dataclasses.fields来获取数据类上的字段对象并进行检查。
field_types = {field.name: field.type for field in fields(MyClass)}

__annotations__fields都不能解析字符串注释。如果你想解析字符串注释,最好的方法可能是使用typing.get_type_hintsget_type_hints将包括ClassVars和InitVars,所以我们使用fields来过滤它们:

resolved_hints = typing.get_type_hints(MyClass)
field_names = [field.name for field in fields(MyClass)]
resolved_field_types = {name: resolved_hints[name] for name in field_names}

1
此外,数据类支持继承,但__annotations__无法解析父类中的字段。 - ollik1
获取每个字段的默认值怎么样? - Eduardo Pignatelli
1
@EduardoPignatelli: field.default(如果没有默认值,则为dataclasses.MISSING)。 - user2357112

54
from dataclasses import dataclass

@dataclass
class MyClass:
    id: int = 0
    name: str = '' 

myclass = MyClass()

myclass.__annotations__
>> {'id': int, 'name': str}
myclass.__dataclass_fields__
>> {'id': Field(name='id',type=<class 'int'>,default=0,default_factory=<dataclasses._MISSING_TYPE object at 0x0000000004EED668>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD),
 'name': Field(name='name',type=<class 'str'>,default='',default_factory=<dataclasses._MISSING_TYPE object at 0x0000000004EED668>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),_field_type=_FIELD)}

另外一方面需要注意的是:

myclass.__dataclass_params__
>>_DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False)

2
作者花了很多心思来格式化一个正确的答案回答这个问题。你能指出一些文件,说明不鼓励写与评论重叠的答案吗? - robx
2
我不太想像那样使用私有字段。我需要类似于检查模块签名对象的东西。 - Kamyar
8
请注意,如果您的版本不是3.10并且使用了from __future__ import annotations语句,则type字段将是类型的字符串表示而不是类型本身。请注意不要改变原始含义,并使翻译更通俗易懂。 - Wolfgang Pfnür
1
@WolfgangPfnür 感谢您的评论,我现在被这个问题困扰着,一直在想为什么所有的答案都是“不正确”的。 - Quimby
1
@WolfgangPfnür 我也是,这个评论真的救了我一命。这是一个非常讨厌的“特性”。 - Julien Debache

5
dataclasses.py 是一个模块,提供装饰器和函数,用于使用变量注释生成常规类方法。这意味着,在处理类之后,用户定义的字段将使用PEP 526 变量注释语法进行形成。该模块注释可以通过 __annotations__ 属性访问。
根据类型注释的运行时效果,可以通过 __annotations__ 属性或使用typing.get_type_hints来访问已注释的类型,建议使用后者。
请参见下面的一些代码示例:
from typing import Dict, ClassVar, get_type_hints
from dataclasses import dataclass

@dataclass
class Starship:
    hitpoints: int = 50


get_type_hints(Starship) // {'hitpoints': int}
Starship.__annotations__ // {'hitpoints': int}
dataclasses.__annotations__ // The annotations of the dataclasses module.
get_type_hints(get_type_hints)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接