81

Python 3.7 introduces new feature called data classes.

from dataclasses import dataclass

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

When using type hints (annotation) in function parameters, you can easily get annotated types using inspect module. How can I get dataclass field types?

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Kamyar
  • 2,494
  • 2
  • 22
  • 33

3 Answers3

116

Inspecting __annotations__ gives you the raw annotations, but those don't necessarily correspond to a dataclass's field types. Things like ClassVar and InitVar show up in __annotations__, even though they're not fields, and inherited fields don't show up.

Instead, call dataclasses.fields on the dataclass, and inspect the field objects:

field_types = {field.name: field.type for field in fields(MyClass)}

Neither __annotations__ nor fields will resolve string annotations. If you want to resolve string annotations, the best way is probably typing.get_type_hints. get_type_hints will include ClassVars and InitVars, so we use fields to filter those out:

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}
user2357112
  • 260,549
  • 28
  • 431
  • 505
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)}

on a side note there is also:

myclass.__dataclass_params__
>>_DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False)
Serbitar
  • 2,134
  • 19
  • 25
  • 2
    I would rather not to use private fields like that. I need something like inspect module signature object. – Kamyar Aug 21 '18 at 10:58
  • 7
    Pay attention here, if you are not on 3.10 and use `from __future__ import annotations` the `type` field will be the `str` representation of the type, not the `type` itself. – Wolfgang Pfnür Oct 28 '21 at 12:07
  • 1
    @WolfgangPfnür Thank you for this comment, I've been bitten by this right now, wondering why were all the answers "incorrect". – Quimby Jan 03 '22 at 14:28
  • 1
    @WolfgangPfnür Same here, lifesaver comment. This is an extremely nasty 'feature'. – Julien Debache Feb 18 '22 at 13:24
5

The dataclasses.py is the module which provides decorator and functions for generating regular class methods by using of the field annotations. Which means that after processing class, the user defined fields shall be formed using PEP 526 Syntax of Variable annotations. The module annotations is accessible as __annotations__.

According to the Runtime effects of type annotations the annotated types is accessible via __annotations__ attribute or by usage of the typing.get_type_hints, the last one the recommended.

Please see some code samples below:

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)
Andriy Ivaneyko
  • 20,639
  • 6
  • 60
  • 82