On Sun, 26 Jun 2022 at 23:20, Jelle Zijlstra <jelle.zijlstra@gmail.com> wrote:
El sáb, 25 jun 2022 a las 13:44, Chris Angelico (<rosuav@gmail.com>) escribió:
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.
This was added in https://github.com/python/cpython/pull/92204. We didn't add it to eval() because there was no use case at the time, but it would be easy to add the same support to eval() too.
Interesting. That would take a bit of extra work (preprocess the dictionary by checking the function object for its variable names, then lifting those out into the separate tuple), but it could be done. ChrisA