The minimal example
The minimal example of your problem is:
m1=1
def outer_fun(m):
commands = ["m"]
s = {i: eval(r) for i, r in enumerate(commands)}
outer_fun(m1)
Which gives:
Traceback (most recent call last):
File "/tmp/pycharm_project_53/eval.py", line 7, in <module>
outer_fun(m1)
File "/tmp/pycharm_project_53/eval.py", line 5, in outer_fun
s = {i: eval(r) for i, r in enumerate(commands)}
File "/tmp/pycharm_project_53/eval.py", line 5, in <dictcomp>
s = {i: eval(r) for i, r in enumerate(commands)}
File "<string>", line 1, in <module>
NameError: name 'm' is not defined
Simplify the dict comprehension operation
The dict comprehension makes the problem complex and conceals the essence, let's get rid of it.
- According to the PEP 274, the expression in you question is equivalent to
s = dict([(i, eval(r)) for i, r in enumerate(commands)])
, where the [(i, eval(r)) for i, r in enumerate(commands)]
causes the problem.
- I think the list comprehesion used above will use lambda function.You can get rid of the list comprehension, perhpas get something like
s = list(map(lambda i, r: (i, eval(r)), range(len(commands)), commands))
(EDIT: this is not accurate, see my question Is list comprehension implemented via map and lambda function?, they are not the same, but they work in a similar way; Perhaps one can still uses the lambda
to understand the behavior of list comprehension if doesn't want to refer to python assembly). And just applying the lambda function (lambda i, r: (i, eval(r)))(0, "m")
will result in problems!
- Using an explict function definition to replace the lambda function that cause the problem. which gives:
m1=1
def outer_fun(m):
commands = ["m"]
# All of the following lines fails:
# s = {i: eval(r) for i, r in enumerate(commands)}
# s = dict([(i, eval(r)) for i, r in enumerate(commands)])
# s = [(i, eval(r)) for i, r in enumerate(commands)]
# s = list(map(lambda i, r: (i, eval(r)), range(len(commands)), commands))
# (lambda i, r: (i, eval(r)))(0, "m")
def inner_func(i, r):
return (i, eval(r))
inner_func(0, "m")
outer_fun(m1)
And it is just like:
m1=1
def outer_fun(m):
def inner_func():
return (0, eval("m"))
inner_func()
outer_fun(m1)
A most simplified version
A most simplified version will be:
m1=1
def outer_fun(m):
def inner_func():
eval("m")
inner_func()
outer_fun(m1)
Which give:
Traceback (most recent call last):
File "/tmp/pycharm_project_53/eval.py", line 7, in <module>
outer_fun(m1)
File "/tmp/pycharm_project_53/eval.py", line 6, in outer_fun
inner_func()
File "/tmp/pycharm_project_53/eval.py", line 5, in inner_func
eval("m")
File "<string>", line 1, in <module>
NameError: name 'm' is not defined
Why the "most simplified version" raise exception
You can refer to the doc of eval. If arguments globals and locals are not passed to the eval
, the eval
is executed with the globals and locals in the environment where eval() is called. However, m
is not the local variable of inner_func
, nor a global variable(but m1
is a global variable).
Another thing that may be insteresting is that if you replace the eval('m')
with print(m)
, the inner_func
can print without any problem, this is because m
is the local variable of the outer_func
that the inner_func
can access.
You can check the globals and locals with the function globals()
and locals()
:
m1=1
def outer_fun(m):
print("local in outer:")
print(locals())
print("global in outer:")
print(globals())
def inner_func():
print("local in inner:")
print(locals())
print("global in inner:")
print(globals())
eval("m")
inner_func()
outer_fun(m1)
Which gives:
local in outer:
{'m': 1}
global in outer:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f0d85ad0eb0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/tmp/pycharm_project_53/11.py', '__cached__': None, 'm1': 1, 'outer_fun': <function outer_fun at 0x7f0d85ad6280>}
local in inner:
{}
global in inner:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f0d85ad0eb0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/tmp/pycharm_project_53/11.py', '__cached__': None, 'm1': 1, 'outer_fun': <function outer_fun at 0x7f0d85ad6280>}
How to fix
You should store the globals and locals, and then pass them to the eval
.
m1=1
def outer_fun(m):
commands = ["m"]
globs = globals()
locs = locals()
s = {i: eval(r, globs, locs) for i, r in enumerate(commands)}
print(s)
outer_fun(m1)
Futher reading
These questions may be related:
- NameError using eval inside dictionary comprehension
- Python Name Error in dict comprehension when called inside a function
- eval fails in list comprehension