Why does accessing function arguments with eval
in a dict comprehension fail?
ARGS1 = ('a1', 'b1')
def foo1(a1, b1):
return {arg:eval(arg) for arg in ARGS1}
print foo1("A1", "B1") # NameError: name 'a1' is not defined
The same thing in a list comprehension is fine:
ARGS2 = ('a2', 'b2')
def foo2(a2, b2):
return [eval(arg) for arg in ARGS2]
print foo2("A2", "B2") # OK, print: ['A2', 'B2']
It also works well without a function :
ARGS3 = ('a3', 'b3')
a3, b3 = ("A3", "B3")
print {arg:eval(arg) for arg in ARGS3} # OK, print: ['A3', 'B3']
Or if globals are defined :
ARGS4 = ('a4', 'b4')
a4, b4 = ("A4", "B4")
def foo4():
return [eval(arg) for arg in ARGS4]
print foo4() # OK, print: ['A4', 'B4']
This really looks like a bug, but maybe I'm missing something here.
(EDITED to include without-function & with-globals examples)
A dict comprehension is executed in a new scope, like a function.
As such, the expression locals are limited to just those named in the loop, in this case
arg
. The parent function locals are not considered, because closures are only bound at compile time. Names referenced byeval()
cannot make use of closures.The following also doesn't work:
The names
a
andb
are not available to the innerfoo
function unless the compiler has determined that that function actually needs access to them:Here, the line
a, b
can only be referring to the parent scope locals (they are not assigned to infoo()
itself), so the compiler has created closures for these and the names have become available as locals withinfoo()
.List comprehensions in Python 2 do not have their own namespace, an omission corrected in Python 3 and not extended to dict and set comprehensions. See Python list comprehension rebind names even after scope of comprehension. Is this right?