Arun Chaganty
Arun Chaganty

Reputation: 381

How do you get type annotations from subclasses when inheriting?

I am trying to build a typed dictionary class, similar to NamedTuple but with the ability to subclass said tuples. Unfortunately, I find that in the base constructor where I want enforce types, I do not have access to the type annotations of subclasses.

Here's a minimal example:

class TypedDict:
  def __init__(self, **kwargs):
    print(self.__annotations__)

    for k, v in kwargs.items():
      # Check if k is in self.__annotations__
      # Check that v has the same type as self.__annotations__[k]
      pass

class A(TypedDict):
  field_a: int = 3 

class B(A):
  field_b: int = 3 

And on the console:

>>> a = A()
{'field_a': <class 'int'>}
>> b = B()
{'field_b': <class 'int'>}  # Where is 'field_a'?

How do I get the constructor in TypedDict.__init__ to also see the annotations of A?

Upvotes: 0

Views: 1128

Answers (2)

Arun Chaganty
Arun Chaganty

Reputation: 381

Ah, I have a workaround solution using the class mro.

class TypedDict:
  def __init__(self, **kwargs):
    annotations = {}
    for cls in self.__class__.mro():
        # __annotations__ is only present when the class has defined annotations.
        annotations.update(getattr(cls, "__annotations__", {}))
    print(annotations)

    for k, v in kwargs.items():
      # Check if k is in self.__annotations__
      # Check that v has the same type as self.__annotations__[k]
      pass

class A(TypedDict):
  field_a: int = 3 

class B(A):
  field_b: int = 3 

On the console:

>>> a = A()
{'field_a': <class 'int'>}
>> b = B()
{'field_a': <class 'int'>, 'field_b': <class 'int'>}

Upvotes: 0

user2357112
user2357112

Reputation: 281748

You're looking for typing.get_type_hints, not __annotations__. typing.get_type_hints will merge the annotations of all classes in the MRO, as well as resolving string annotations.

type_hints = typing.get_type_hints(type(self))

Upvotes: 3

Related Questions