在从Py2升级到Py3的代码中,我遇到了一个奇怪的问题,我无法解释。类定义中的列表推导式可以在Python 2和3中都作为for
子句引用其他类级变量。然而,如果这些变量是if
子句的一部分,则它们会在Python 3中抛出NameError
,但在Python 2中却可以正常工作。
我找到了一些相关的问题,但它们似乎并不能完全解释这个问题。在定义中引用其他类属性的字典类属性是一个类似的问题,在lambda中,但似乎不依赖于Python版本。此外为什么列表推导中会有NameError?,但与类的范围有关而非调试器。
以下代码在Python 2.7.16上可以正常工作,但在Python 3.7.4上失败:
class A(object):
a = [1, 2, 3]
b = [n for n in a if n in a]
print(A.b)
在 Python 2 中,我得到了:
[1, 2, 3]
在Python 3中,我得到:
Traceback (most recent call last):
File "list_comprehension_closure.py", line 3, in <module>
class A(object):
File "list_comprehension_closure.py", line 5, in A
b = [n for n in a if n in a]
File "list_comprehension_closure.py", line 5, in <listcomp>
b = [n for n in a if n in a]
NameError: name 'a' is not defined
然而,以下内容在Python 2和3中都可以正常工作,唯一的区别在于
if
子句。class A(object):
a = [1, 2, 3]
b = [n for n in a]
print(A.b)
此外,以下内容在python 2和3中都适用,唯一的区别是推导式定义在class
块之外:
a = [1, 2, 3]
b = [n for n in a if n in a]
print(b)
我知道Python 3中的闭包和列表解析有一些变化,但我没有看到任何可以解释这种差异的地方。 编辑: 为了更清晰,我不是在寻找解决方法。正如我最后一个例子所示,我知道将变量移出类作用域可以解决问题。然而,我想知道的是为什么Python 3中的行为发生了改变。
> cat list_comprehension_closure.py class A(object): a = [1, 2, 3] b = [n for n in a if n in a] print(A.b) > python2.7 list_comprehension_closure.py [1, 2, 3] > python3.7 list_comprehension_closure.py ... NameError: name 'a' is not defined
- jbryana
会失败,这是有道理的。我可能在字节码分解方面漏掉了一些东西,但我的理解是,我的第二个例子,在for
语句中仅引用a
应该 在Python 3中失败,但它没有。 - jbryan