Open JelleZijlstra opened 1 month ago
We could internally create a more streamlined "mini-codeobject" and materialize the real code object only when necessary
Maybe a variation of this could be that we create a bytestring with the marshalled code object, and unmarshal it only on demand.
Tools like functools.wraps would unnecessarily force materialization of the annotate function. Not sure there's an elegant solution for this.
The proposal in #124342 would actually fix this, because we make it so the wrapper accesses the wrapped function's .__annotate__
lazily.
Currently, when a class, function, or module has any annotations, we always generate an
__annotate__
function object at import time. A function object takes 168 bytes. But in most cases, all of the relevant fields on an__annotate__
function are predictable (there's no docstring and no defaults or kwdefaults, the name is__annotate__
, etc.). So we could save significant memory by constructing only a smaller object and constructing the function on demand when somebody asks for it (by accessing__annotate__
).We need the following to create an
__annotate__
function object:__annotate__
descriptor can't easily get to the globals dict. To do this, we may need a new bytecode that just loads the current globals.I am thinking of a format where
__annotate__
can be any of the following:__annotate__
getters would have to recognize the second and third cases and translate them into function objects on the fly. As a result, users accessing.__annotate__
would never see the tuple, though those who peek directly into a module or class's__dict__
might.Other related opportunities for optimization:
functools.wraps
would unnecessarily force materialization of the__annotate__
function. Not sure there's an elegant solution for this.