【问题标题】:Why does list comprehension closure throw `NameError` in python 3 but not in python 2? [duplicate]为什么列表推导闭包会在 python 3 中抛出 `NameError` 而在 python 2 中却没有? [复制]
【发布时间】:2020-01-02 05:58:30
【问题描述】:

在将代码从 Py2 升级到 Py3 时,我遇到了一个我无法解释的奇怪现象。类定义中的列表推导可以引用 python 2 和 3 中的其他类级别变量作为for 子句的一部分。但是,如果变量是 if 子句的一部分,它们会在 python 3 中抛出 NameError,但在 python 2 中可以正常工作。

我发现了一些相关的问题,但它们似乎并不能完全解释这个问题。 Dictionary class attribute that refers to other class attributes in the definition 是 lambdas 中的一个类似问题,但似乎与 python 版本无关。另外Why am I getting a NameError in list comprehension (Python)?,但与调试器中的作用域而不是类有关。

以下代码在 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 中这种行为发生了变化。

【问题讨论】:

  • 将python 2代码复制到你的python 3文件并再次运行
  • @U10-Forward 虽然在发布后的 10 分钟内发现任一版本的 python 都发生了重大变化,这让我感到惊讶,但为了成为一项好运动,我在 python 中运行了相同的代码2 和 3 再次得到相同的结果:``` > cat list_comprehension_closure.py class A(object): a = [1, 2, 3] b = [n for n in a if n in a] print(Ab) > python2.7 list_comprehension_closure.py [1, 2, 3] > python3.7 list_comprehension_closure.py ... NameError: name 'a' is not defined ```
  • @hunzter 谢谢,我在搜索中没有看到这个问题。但是,它并不能完全解决问题。关于为什么我在 if 子句中引用 a 的第一个示例失败的解释是有道理的。我可能在字节码分解中遗漏了一些东西,但我对此的阅读似乎表明我的第二个示例 a 仅在 python 3 中的 for 子句 should fail 中引用,但是它没有。
  • @hunzter,实际上经过仔细阅读,链接的问题确实解决了我的第二个示例为何有效。 “无论 Python 版本如何,理解或生成器表达式的一部分都在周围范围内执行。那将是最外层可迭代的表达式。”

标签: python python-3.x


【解决方案1】:

在我看来,这似乎是变量a 的范围不合适的问题。通过将a 放在更高的范围内,以下工作:

a = [1, 2, 3]
class A(object):
    b = [n for n in a if n in a]

print(A.b)

【讨论】:

  • 我知道有办法解决这个问题,事实上,为了让代码正常工作,我采用了你建议的第一种方法的变体。您的第二种方法在任一 python 版本中都不起作用,因为 A 在被引用时尚未完成定义。在 python 2 和 3 中,它都会抛出 NameError: name 'A' is not defined 但是,我试图了解 python 2 和 3 之间发生了什么变化的为什么,这可以解释行为上的差异。
  • 我删除了第二部分,因为你是对的,它不起作用。我一定没有先清除所有东西。我不知道为什么。
猜你喜欢
  • 1970-01-01
  • 2023-01-16
  • 1970-01-01
  • 1970-01-01
  • 2015-12-26
  • 2014-12-27
  • 1970-01-01
  • 2012-04-04
  • 1970-01-01
相关资源
最近更新 更多