-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Compiling Nested Functions
Nested functions can access variables defined in an outer function:
def f(x: int) -> None:
def g() -> None:
print(x) # Refer to x in the outer function
g()
To support this, mypyc creates a hidden environment class, and instances of this class hold locals that are accessed in nested functions. The nested function is also represented as an instance of a generated class which has a reference to the environment. The example above behaves like this when compiled, roughly:
class f_env: # Generated class, implementation detail
x: int
g: g_f_obj
class g_f_obj: # Generated class, implementation detail
__mypyc_env__: f_env
def __call__(self) -> None:
print(self.__mypyc_env__.x)
def f(x: int) -> None:
_env = f_env()
_env.x = x
g = g_f_obj()
g.__mypyc_env__ = _env
_env.g = g
g()
The environment class _env
also holds a reference to the nested function g
to allow recursive calls in g
, and mutually recursive calls if there are multiple nested functions. The nested function has no direct access to the locals of the outer function, other than those accessible via the environment. In this case there are no recursive calls so we could drop the g
attribute. It's sometimes needed and we generate it always, for simplicity.
The environment and the nested function object are separate instances so that locals from an outer function can be shared between multiple nested functions. If there was only a single nested function, mypyc could merge the environment and the function object (but this is not implemented).
-
mypyc/irbuild/envclass.py
(environment classes) -
mypyc/irbuild/callable_class
(nested function objects)