Reputation: 1839
The below code snippet is getting different outputs from Python 2.7 and 3.3.
data = {'_out':[1,2,3,3,4]}
codes = ['_tmp=[]',
'[_tmp.append(x) for x in _out if x not in _tmp]',
'print(_tmp)']
for c in codes:
exec(c,{},data)
Output from Python 2.7:
[1,2,3,4]
Output from Python 3.3:
Traceback (most recent call last):
File "test.py", line 8, in <module>
exec(c,{},data)
File "<string>", line 1, in <module>
File "<string>", line 1, in <listcomp>
NameError: global name '_tmp' is not defined
To fix the error in Python 3.3, I simply set the globals to be as the same as locals, that is exec(c,data,data)
. Any idea why Python 3.3 is not behaving as that in 2.7?
Upvotes: 4
Views: 94
Reputation: 66825
It appears to be known and desired behaviour, see issue 13557 https://bugs.python.org/issue13557
and further in
https://docs.python.org/3/reference/executionmodel.html#interaction-with-dynamic-features
The eval() and exec() functions do not have access to the full environment for resolving names. Names may be resolved in the local and global namespaces of the caller. Free variables are not resolved in the nearest enclosing namespace, but in the global namespace.
You can solve the above problem by not using list comprehension with local variables method calls, or by providing variables through global scope
data = {'_out':[1,2,3,3,4]}
codes = ['_tmp=[]', """
for x in _out:
if x not in _tmp:
_tmp.append(x)
""",
'print(_tmp)']
for c in codes:
exec(c, {}, data)
data = {'_out':[1,2,3,3,4]}
codes = ['_tmp=[]',
'[_tmp.append(x) for x in _out if x not in _tmp]',
'print(_tmp)']
for c in codes:
exec(c, data)
Upvotes: 2