Reputation: 70
Code 1: for loop
def foo():
one = '1'
two = '2'
three = '3'
d = {}
for name in ('one', 'two', 'three'):
d[name] = eval(name)
print(d)
foo()
output:
{'one': '1', 'two': '2', 'three': '3'}
Code 2: dict comprehension
def foo():
one = '1'
two = '2'
three = '3'
print({name: eval(name) for name in ('one', 'two', 'three')})
foo()
output:
NameError: name 'one' is not defined
Code 3: add global keyword
def foo():
global one, two, three # why?
one = '1'
two = '2'
three = '3'
print({name: eval(name) for name in ('one', 'two', 'three')})
foo()
output:
{'one': '1', 'two': '2', 'three': '3'}
Dict comprehensions and generator comprehensions create their own local scope. According to the definition of the closure (or not the closure here), but why can't Code 2 access the variable one[,two,three]
of the outer function foo
? However, Code 3 can successfully create a dictionary by setting the variable one[,two,three]
to global?
So is it because the eval
function and the dict comprehensions have different scopes?
Hope someone help me, I will be grateful!
Upvotes: 0
Views: 369
Reputation: 8180
To understand whats happening, try this:
def foo():
global one
one = '1'
two = '2'
print({'locals, global': (locals(), globals()) for _ in range(1)})
foo()
Output
{'locals, global': ({'_': 0, '.0': <range_iterator object at ...>},
{'__name__': '__main__', '__package__': None, ..., 'one': '1'})}
The builtin eval(expression)
is a shortcut for eval(expression[, globals[, locals]])
.
As you see in the previous output, locals()
is not local symbol table of the function because list/dict comprehensions have their own scope (see https://bugs.python.org/msg348274 for instance).
To get the output you expected, you just have to pass the local symbol table of the function to eval
.
def bar():
one = '1'
two = '2'
three = '3'
func_locals = locals() # bind the locals() here
print({name: eval(name, globals(), func_locals) for name in ('one', 'two', 'three')})
bar()
Output
{'one': '1', 'two': '2', 'three': '3'}
Upvotes: 1