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.
Cheers,
/arry