Search code examples
pythondictionaryobjectinheritancepython-dataclasses

dataclass __annotation__ not working with inheritance


I tried this code:

from dataclasses import dataclass

@dataclass
class Data1:
    d11: str
    d12: float

@dataclass
class Data2:
    d21: str
    d22: float

@dataclass
class D3(Data1, Data2): pass

print(D3.__annotations__)

But the annotations for D3 are only {'d11': <class 'str'>, 'd12': <class 'float'>}. Why does it not include annotations for the Data2 fields?


Solution

  • __annotations__ is for the literal annotations in the class body. It is not intended to represent inheritance at all.
    If, for example, D3 actually has annotations only these are visible:

    @dataclass
    class D3(Data1, Data2):
        a: 3
    
    print(D3.__annotations__)  # {'a': 3}
    

    Note that the "inherited annotations" are available via typing.get_type_hints, which achieves this by "merging all the __annotations__ along C.__mro__ in reverse order". For dataclasses, dataclasses.fields also provides the full Field specifications for a class or instance.


    In your specific case, the __annotations__ are non-empty due to a bug fixed in Python 3.10. Prior to Python 3.10, __annotations__ is only created when there actually are annotations; this means that if a parent class has annotations but a child class does not, the parent annotations are visible via the usual attribute lookup.

    Accessing the actual class' annotation, i.e. circumventing parent attribute lookup

    @dataclass
    class D3(Data1, Data2):
        pass
    
    print(D3.__dict__["__annotations__"])
    

    will trigger KeyError: '__annotations__' in Python 3.9 or earlier, and print {} in Python 3.10 or later.