Python 2.7和3.3中的exec表现不同

4
下面的代码段在Python 2.7和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)

Python 2.7的输出:

[1,2,3,4]

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

为了修复 Python 3.3 中的错误,我只需将全局变量设置为与本地变量相同,即 exec(c,data,data)。有任何想法为什么 Python 3.3 的表现与 2.7 不同?

1
更有趣的是,当使用简单的“for”循环而不是推导式时,它可以正常工作(顺便说一下 - 推导式是操作列表的不正确方式)。 - lejlot
没错,for循环是有效的,这就是我以为那是一个bug的原因。感谢你提供了如此全面的答案。问题编号13557是个很好的发现! - pangyuteng
1
在3.0中有两个相关的变化。Exec现在是一个函数而不是语句。推导式的主体现在在一个单独的命名空间中执行,而不是包含推导式的命名空间中执行。 - Terry Jan Reedy
1
还可以查看Python中eval、exec和compile的区别是什么?(https://dev59.com/t3E95IYBdhLWcg3wp_gg),尤其是Antti Haapala所写的杰出答案(https://dev59.com/t3E95IYBdhLWcg3wp_gg#29456463)。 - PM 2Ring
1个回答

2
似乎这是已知且被期望的行为,参见问题13557 https://bugs.python.org/issue13557以及后续内容。

https://docs.python.org/3/reference/executionmodel.html#interaction-with-dynamic-features

eval()和exec()函数无法访问完整的环境以解析名称。名称可以在调用者的本地和全局命名空间中解析。自由变量不是在最近的封闭命名空间中解析,而是在全局命名空间中解析。您可以通过不使用带有局部变量方法调用的列表推导或通过全局范围提供变量来解决上述问题。
使用循环代替。
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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接