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__': , '__name__': '__main__', '__loader__': , 'b': <__main__.B object at 0x7f406f55d4a8>, '__doc__': None, '__package__': None, 'B': , '__spec__': None}
{'.0': , '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 python-3.x) 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 "", line 1, in
NameError: name 'x' is not defined