【问题标题】:How to Avoid Leaking of Python Assignment Expressions in Comprehensions如何避免理解中的 Python 赋值表达式泄漏
【发布时间】:2020-10-04 09:23:27
【问题描述】:

Effective Python一书中,作者建议使用赋值表达式来避免理解中的冗余,例如:

def fun(i):
    return 2 * i

result = {x: y for x in [0, 1, 2, 3] if (y := fun(x)) > 3}

而不是

result = {x: fun(x) for x in [0, 1, 2, 3] if fun(x) > 3}

result 的值为 {2: 4, 3: 6}

作者说

如果一个推导在推导的值部分使用海象运算符并且没有条件,它会将循环变量泄漏到包含范围中。 [...] 最好不要泄漏循环变量,因此我建议仅在推导式的条件部分使用赋值表达式。

但是,在上面的示例中,y 在程序结束时设置为 6。所以,赋值表达式中的变量泄露了,虽然它是在条件中定义的。

列表推导也会发生同样的事情:

>>> _ = [(x, leak) for x in range(4) if (leak := 2 * x) > 3]
>>> leak
6

甚至对于生成器表达式:

>>> it = ((x, leak) for x in range(4) if (leak := 2 * x) > 3)
>>> next(it)
(2, 4)
>>> leak
4
>>> next(it)
(3, 6)
>>> leak
6

我错过了什么?有什么办法可以完全避免在理解中的赋值表达式泄漏?

【问题讨论】:

  • Python 没有变量的块作用域,唯一的作用域是函数和类定义。所以文中的解释对我来说真的没有意义。
  • 生成器和列表推导也没有建立新的范围。
  • 这是旧的,但有一些关于 python 3.X 的更新:stackoverflow.com/questions/4198906/…
  • @Barmar 事实上他们这样做了:x = 1; print([locals() for y in range(1)]) 打印 y 并且不从外部范围打印 x
  • @kaya3 你是对的。更重要的是:[y for y in range(1)] 不会将y 泄漏到包含范围内。直到您发表评论,我才意识到这一点

标签: python python-assignment-expression


【解决方案1】:

在 Python 中,不可能泄漏循环变量

与 C 或 Java 等其他语言不同,Python 在 iffor 块中没有单独的范围。因此,当您在 if 语句、for 循环或列表推导中使用 := 运算符时,分配的变量将 在函数或类定义的其余部分中。这也意味着在每个for 循环之后,循环变量仍将在作用域内并包含循环最后一次迭代的值。

如果Effective Python的作者认为这是一件坏事,我不同意他的看法。 “泄漏”循环变量可能非常有用!考虑以下示例:

while line := f.readLine():
    if 'Kilian' in line:
        break

print('This is the first line that contains your name: ', line)

但是,此规则有一个例外:在列表推导中进行的隐式赋值有自己的范围:

>>> [x for x in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

这个例外可能是您的困惑的根源。这只是一种特殊情况,不适用于在列表推导中使用 := 时。

【讨论】:

  • 就代码的行为而言,这并没有错,但从概念上讲,它具有误导性。理解是在它们自己的堆栈帧中评估的,因此“隐式赋值”不会泄露,原因与函数中的局部变量不会泄露给调用者的原因相同。您可以通过在理解中调用 locals() 来检查这一点。海象运算符需要该行为的特殊情况才能故意泄漏变量。
猜你喜欢
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
  • 2020-05-13
  • 1970-01-01
  • 1970-01-01
  • 2015-08-27
  • 2018-10-27
  • 1970-01-01
相关资源
最近更新 更多