问题
I want to evaluate a lambda expression using the built-in eval
function, with a variable y
defined in the 'locals' argument. Sadly the result function doesn't work:
>>>x = eval('lambda: print(y)',{},{'y':2})
>>>x()
Traceback (most recent call last):
File "<pyshell#75>", line 1, in <module>
x()
File "<string>", line 1, in <lambda>
NameError: name 'y' is not defined
But with y
defined in the 'globals' argument, it does work:
>>> x = eval('lambda: print(y)', {'y': 2},{})
>>> x()
2
As I understand, the lambda expression should have captured the whole current frame including all the variables defined in the globals and locals arguments.
So why does Python behave like this?
回答1:
Quite simply: passing a populated locals
directory doesn't change the way python parses a function code and decides which names are locals and which are globals.
Local names are arguments names and names that are bound within the function's body and not explicitely declared globals or non-locals. Here, y
is not an argument, and is not bound within the function's body (which is impossible in a lambda
anyway) so it is marked by the compiler as global.
Now those global and local environment are those used to evaluate the expression itself (here the full 'lambda: print(y)' expression), not "the local environment for the lambda's body", so even if there was a way to make y
local to the function's body (hint: there's one - but it won't solve your problem) this name would still not be "automagically" set to the the 'y' value in the locals dict passed to eval
.
BUT this is actually not a problem - the function created by eval("lambda: y", {"y":42})
captures the globals dict passed to eval
and uses it in place of the module/script/whatever globals, so it will work as expected:
Python 3.4.3 (default, Nov 28 2017, 16:41:13)
[GCC 4.8.4] on linux
>>> f = eval("lambda: y+2", {'y':2}, {})
>>> f()
4
>>> y = 42
>>> f()
4
Now you have the explanation, I whish to add that eval()
is very dangerous and most often than not there's a much better solution, depending on the exact problem you're trying to solve. You didn't provide any context so it's impossible to tell more but I have to say I actually have a hard time thinking of a concrete use-case for such a thing as f = eval("lambda: whatever")
.
回答2:
x = eval('lambda: print(y)',{},{'y':2})
is not equal to this line x = eval('lambda: print(y)', {'y': 2},{})
in first part change params order and it should work
来源:https://stackoverflow.com/questions/50579541/why-lambda-in-eval-function-cant-closure-the-variables-in-the-user-defined-loc