pangyuteng
pangyuteng

Reputation: 1839

python exec behaving differently between 2.7 and 3.3

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

Answers (1)

lejlot
lejlot

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

Loops instead

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)

Global environment

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

Related Questions