Accessing The Annotations Dict Of An Object In Python 3.10 And Newer
Python 3.10 adds a new function to the standard library: inspect.get_annotations(). In Python versions 3.10 and newer, calling
this function is the best practice for accessing the annotations dict
of any object that supports annotations. This function can also
“un-stringize” stringized annotations for you.
If for some reason inspect.get_annotations() isn’t viable for your use case, you may access the __annotations__ data member manually.
Best practice for this changed in Python 3.10 as well: as of Python
3.10, o.annotations is guaranteed to always work on Python functions, classes, and modules. If you’re certain the object you’re
examining is one of these three specific objects, you may simply use
o.annotations to get at the object’s annotations dict.
However, other types of callables–for example, callables created by functools.partial()–may not have an annotations attribute
defined. When accessing the annotations of a possibly unknown
object, best practice in Python versions 3.10 and newer is to call
getattr() with three arguments, for example getattr(o,
'annotations', None).
Before Python 3.10, accessing annotations on a class that defines no annotations but that has a parent class with annotations
would return the parent’s annotations. In Python 3.10 and newer,
the child class’s annotations will be an empty dict instead.
In Python 3.9 and older, accessing the annotations dict of an object is much more complicated than in newer versions. The problem is a
design flaw in these older versions of Python, specifically to do with
class annotations.
Best practice for accessing the annotations dict of other
objects–functions, other callables, and modules–is the same as best
practice for 3.10, assuming you aren’t calling
inspect.get_annotations(): you should use three-argument getattr() to
access the object’s annotations attribute.
Unfortunately, this isn’t best practice for classes. The problem is
that, since annotations is optional on classes, and because
classes can inherit attributes from their base classes, accessing the
annotations attribute of a class may inadvertently return the annotations dict of a base class.
Python
@dataclass
class Parent():
p: int = 42
@dataclass
class Child(Parent):
c: int = 0
class Foo(Child):
pass
parent = Parent()
child = Child()
foo = Foo()
output 3.7
print(f"Parent object annotations: {parent.__annotations__}")
print(f"Child object annotations: {child.__annotations__}")
print(f"Foo object annotations: {foo.__annotations__}")
Parent object annotations: {'p': <class 'int'>}
Child object annotations: {'c': <class 'int'>}
Foo object annotations: {'c': <class 'int'>} # <-- Wrong :(
python 3.10
print(f"Parent object annotations: {inspect.get_annotations(type(child))}")
print(f"Child object annotations: {inspect.get_annotations(type(parent))}")
print(f"Foo object annotations: {inspect.get_annotations(type(foo))}")
Parent object annotations: {'c': <class 'int'>}
Child object annotations: {'p': <class 'int'>}
Foo object annotations: {}
Source: https://docs.python.org/3/howto/annotations.html