Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork33.7k
Description
I like theclosure argument to exec() and eval() added to 3.11.
But, ... I have some complaints about it as it stands at present.
The closure argument specifies a closure–a tuple of cellvars. It’s only valid when the object is a code object containing free variables. The length of the tuple must exactly match the number of free variables referenced by the code object.
Which free variables?? In the exec() code, or in the frame where exec() is called from?
Where do I get or make cellvars?
In what order do the cellvars go?
Without answering these questions, the new feature will be useless to a programmer.
The answers, as well as I can figure out, are:
- The whole business only applies if exec() is executing a code object compiled from atarget which is function or lambda.
- EDIT: The purpose of the feature is to allow the executed code to assign or delete a free variable (which must be declared nonlocal), or get the value of a free variable.
- This code object is intarget.code.
- The names of the free variables are intarget.code.co_freevars.
- The appropriate closure argument is intarget.closure. This has the cellvars corresponding to the free variables named intarget.code.co_freevars, and in the same order.
- You can also use a different closure from a different target, provided it was compiled in the same scope, and contains the same variable names (even if they appear in a different order)
- The order in which free variable names appear is fixed in the enclosing scope, so different targets will have their free variable names in the same order.
- If the code binds, rebinds, or deletes one of the free variables (which must be declared as nonlocal in the code), that change will show up in that variable in the calling scope.
- There is no way in Python to create a cellvar other than the above. In the future, if some means is provided to create a cellvar for a free variable, then that cellvar will be tied to that variable in the scope in which it was created.
Suggested enhancement:
I propose a new builtin function calledclosure(). This will return a dict of cellvars forall free variables in the current scope. The same dict can be used by exec() with different code objects compiled in the scope.
closure() may be called as closure(names, [name, ]...), which will return a dict for just those variables (which must be free variables in the current scope).
exec() willalso accept a closure argument which is a dict of variable names to cellvar objects. Thismay include names which arenot free variables in the code object being executed, but it must includeall names whichare free variables. Any stores or deletes performed in the executed code will show up in the cellvar values. If they reflect the actual free variables in the scope, the changes will show up in the variables in the calling scope.
Why not provide a closure to eval() also? eval() can already evaluate an expression with free variables. However, eval() of an assignment expression doesn't work as expected. The assignment is made in the locals dict given to eval(). Passing a closure would allow the assignment to be made to a free variable.
Provide methods on cellvar objects to bind, rebind, or delete the cell_contentsin-place. This will be visible in the free variable which the cellvar corresponds to. This would be more convenient than performing the corresponding exec().