eval fails in list comprehension [duplicate]

By using list comprehension, you actually define a new scope. Indeed if we alter the list comprehension to:

out2 = [print(globals()) or print(locals()) or eval(cmd) for cmd in ['self.b']]

we force Python to print the local and global variables before making the eval(..) call, and we obtain something like:

{'__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, 'b': <__main__.B object at 0x7f406f55d4a8>, '__doc__': None, '__package__': None, 'B': <class '__main__.B'>, '__spec__': None}
{'.0': <list_iterator object at 0x7f406f55df28>, 'cmd': 'self.b'}

So as local variables we only have a .0 and a cmd.

You can however pass self to the list comprehension by using:

globs = globals()
locs = locals()
out2 = [eval(cmd,globs,locs) for cmd in ['self.b']]

So now eval(..) will use the local and global variables as defined in the scope of the function. Since we use locs and globs. Python will pass references to these dictionaries to the eval(..) call.

Finally a warning as with every use of eval(..): eval is a dangerous function. You better use it only if you really need it.

An additional side effect of this additional scope (introduced in ) is that the loop variable does not leak: after the list comprehension cmd is cleaned up: you can no longer access it (usually it would hold the last element it has handled). For example:

>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

Leave a Comment