On 1/16/21 8:41 AM, Nick Coghlan wrote:
Could you get the best of both worlds by making __annotations__ an auto-populating descriptor on "type", the way it is on functions?

Continue to add a non-empty annotations dict to the class dict eagerly, but only add the empty dict when "cls.__annotations__" is accessed.

I think that'll work though it's a little imprecise.  Consider the best practice for getting class annotations, example here from Lib/dataclasses.py:

cls_annotations = cls.__dict__.get('__annotations__', {})

What happens when that current best practice code meets your proposed "lazy-populate the empty dict" approach?

So the code will continue to work, even though it's arguably a little misguided.  If anybody distinguished between "annotations are unset" and "annotations are set to an empty dict", that code would fail, but I expect nobody ever does that.

Two notes about this idea.  First, I think most people who use this best-practices code above use it for modules as well as classes.  (They have two code paths: one for functions, the other for not-functions.)  But everything I said above is true for both classes and modules.

Second, I think this is only sensible if, at the same time, we make it illegal to delete cls.__annotations__.  If we lazy-populate the empty dict, and a user deletes cls.__annotations__, and we don't remember some extra state, we'd just re-"lazy" create the empty dict the next time they asked for it.  Which is actually what functions do, just lazy-repopulate the empty annotations dict every time, and I'm not keen to bring those semantics to classes and modules.