Reputation: 133
I have a list of variables and values that I assign to them using an eval statement.
I am trying to use a dictionary comprehension to match the variable with the evaluated value.
When I use a for loop for i in range(0,10) where len(ChosenVarNameList) = 10:
dictinitial = {}
for i in range (0,len(ChosenVarNameList)):
dictinitial[ChosenVarNameList[i]] = eval("%s" %ChosenVarNameList[i])
I can create the dictionary.
When I reference individual indexes also I can see the dictionary populating correctly (with the code below).
dictinitialnew = {ChosenVarNameList[0] : (eval("%s"
%ChosenVarNameList[0])).
However when I try a dictionary comprehension like the code below:
dictinitialnew = {ChosenVarNameList[i] : (eval("%s" %ChosenVarNameList[i]))
for i in range (0,len(ChosenVarNameList)) }
I get code saying the first variable name let's say 'Code1' is not defined. Is there a way to do this using a dictionary comprehension or is there an alternative that I must use to get around this problem.
Thanks in advance.
Upvotes: 0
Views: 60
Reputation: 155497
Your problem is due to dict
comprehensions introducing nested scope. For most practical purposes, a dict
comprehension in a function like:
def myfunc(iterable, y):
return {x: y for x in iterable}
is implemented as something very similar to:
def myfunc(iterable, y):
def _unnamed_func_(_unnamed_it_):
retval = {}
for x in _unnamed_it_:
retval[x] = y # Note: y is read from nested scope, not passed to inner func
return retval
return _unnamed_func_(iterable) # Note: iterable passed as argument
That _unnamed_func_
, like all functions with closure scope, determines what values from the nested scope are needed at the moment it is defined and folds them into its own closure scope; in this case, it needs y
from the nested scope, but not iterable
(because the first iterable you iterate over is passed to the virtual function as an argument, not through closure scope).
Problem is, eval
is executed with knowledge of only the local and global scopes (as well as implicit knowledge of the builtin scope that all code has); it doesn't know about the nested scope, and since you only reference the variables via eval
, the nested function doesn't know it needs them either.
You can demonstrate the general problem with simpler code:
def outer(x):
def inner():
return eval('x')
return inner
If you try to run that with outer(1)()
(and no x
in global scope), it will die with NameError: name 'x' is not defined
, because x
was not part of the closure scope of inner
, and it was promptly discarded when outer
returned. Changing eval('x')
to just x
allows it to work (it returns 1
in the example case), because without eval
getting in the way, inner
pulls x
into its closure scope at definition time so it's available when inner
is run later.
Your design is a bad one to start with (eval
should not be used for reading simple variable names), dict
comprehensions just make it break completely.
The reason is behaves this way is that the language definitions for comprehensions are built off of the definition of a generator expression, and generator expressions must be implemented with closure scope; since they run lazily, if they didn't use a closure scope to keep nested variables they rely on alive, by the time they are run out the scope might no longer exist. list
comprehensions in Python 2 used to execute without a closure, but it caused some weird artifacts (e.g. running foo = [x for x in y]
in a class definition would give the class a class attribute named x
with the final value x
took in the comprehension) and in Python 3, all comprehensions were changed to use an implicit closure scope (this was only a change for listcomps; dict
and set
comprehensions were added later, and used closure scopes from the start).
Upvotes: 1