Annotations Best Practices — Accessing The Annotations Dict Of An Object In Python 3.9 And Older

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. As an example

This will print the annotations dict from Base, not Derived.

Your code will have to have a separate code path if the object you're examining is a class (isinstance(o, type)). In that case, best practice relies on an implementation detail of Python 3.9 and before: if a class has annotations defined, they are stored in the class's ~type.dict dictionary. Since the class may or may not have annotations defined, best practice is to call the ~dict.get method on the class dict.

To put it all together, here is some sample code that safely accesses the annotations attribute on an arbitrary object in Python 3.9 and before

After running this code, ann should be either a dictionary or None. You're encouraged to double-check the type of ann using isinstance before further examination.

Note that some exotic or malformed type objects may not have a ~type.dict attribute, so for extra safety you may also wish to use getattr to access !dict.