On Sun, 26 Jun 2022 at 04:41, Brendan Barnwell <brenbarn@brenbarn.net> wrote:
> In contrast, what I would want out of deferred evaluation is precisely
> the ability to evaluate the deferred expression in the *evaluating*
> scope (not the definition scope) --- or in a custom provided namespace.
> Whether this evaluation is implicit or explicit is less important to
> me than the ability to control the scope in which it occurs. As others
> mentioned in early posts on this thread, this could complicate things
> too much to be feasible, but without it I don't really see the point.
A custom-provided namespace can already be partly achieved, but
working in the evaluating scope is currently impossible and would
require some major deoptimizations to become possible.
>>> expr = lambda: x + y
>>> expr.__code__.co_code
b't\x00t\x01\x17\x00S\x00'
>>> ns = {"x": 3, "y": 7}
>>> eval(expr.__code__, ns)
10
This works because the code object doesn't have any locals, so the
name references are encoded as global lookups, and eval() is happy to
use arbitrary globals. I say "partly achieved" because this won't work
if there are any accidental closure variables - you can't isolate the
lambda function from its original context and force everything to be a
global:
>>> def f(x):
... return lambda: x + y
...
>>> expr = f(42)
>>> eval(expr.__code__, ns)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: code object passed to eval() may not contain free variables
The mere fact that there's a local variable 'x' means that you can't
compile the expression 'x + y'. So maybe there'd need to be some weird
trick with class namespaces, but I'm really not sure what would be
worth doing.
Note that in Python 3.11 exec (but not eval) gains the ability to pass an explicit closure:
>>> def f(x):
... return lambda: print(x + y)
...
>>> l = f(1)
>>> exec(l.__code__, {"y": 3}, closure=(types.CellType(2),))
5
This doesn't quite solve the problem being discussed here, but it may help.