문제

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 by eval() cannot make use of closures.

The following also doesn't work:

>>> ARGS = ('a', 'b')
>>> def bar(a, b):
...     def foo():
...         for arg in ARGS:
...             eval(arg)
...     return foo
... 
>>> print bar("A", "B")()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in foo
  File "<string>", line 1, in <module>
NameError: name 'a' is not defined

The names a and b are not available to the inner foo function unless the compiler has determined that that function actually needs access to them:

>>> def bar2(a, b):
...     def foo():
...         a, b
...         for arg in ARGS:
...             eval(arg)
...     return foo
... 
>>> print bar2("A", "B")()
None
>>> print bar2("A", "B").func_closure
(<cell at 0x1051bac20: str object at 0x104613328>, <cell at 0x1051bacc8: str object at 0x1045971e8>)
>>> print bar2("A", "B").__code__.co_freevars
('a', 'b')

Here, the line a, b can only be referring to the parent scope locals (they are not assigned to in foo() itself), so the compiler has created closures for these and the names have become available as locals within foo().

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?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top