小能豆

任何/所有 python 短路:为什么以下内容不起作用?

py

以下代码应该短路:

any([True, 2+2, False, 2/0])
all([True, 2+2, False, 2/0])

但对于其中的每一个,我都会得到一个ZeroDivisionError: division by zero

我是不是漏掉了什么?为什么会出现错误?


阅读 9

收藏
2024-11-19

共1个答案

小能豆

你的代码之所以会抛出 ZeroDivisionError,是因为 anyall 并不会阻止列表的完全求值。在 Python 中,列表是在传递给函数之前就已经构建好的,这意味着:

  • 列表中的所有元素都会先被计算出来,然后整个列表作为参数传递给 any()all()
  • 因此,2/0 在构造列表时就会被计算,从而导致 ZeroDivisionError

为什么会发生这种情况?

anyall 只对传入的迭代器执行短路,但传递的列表已经是一个已构造好的对象。因此,短路行为仅适用于迭代期间的元素,而不是在构造列表时。

对于你的代码:

any([True, 2+2, False, 2/0])  # 列表在调用 `any()` 前被完全求值。
  • 列表构造时:
  • True -> OK
  • 2+2 -> OK
  • False -> OK
  • 2/0 -> 抛出错误

如何避免这种情况?

为了实现短路,应该改用生成器表达式,因为生成器表达式是惰性求值的,每次只生成一个元素,避免提前计算所有元素。

以下是修复代码的方法:

any(x for x in [True, 2+2, False, 2/0])  # 生成器表达式

或更明确地:

any(x for x in (True, 2+2, False, 2/0))  # 使用元组,避免提前构造列表

短路行为分析

对于 any:
- 一旦找到第一个 True,即停止计算。
- 如果 True 是第一个元素,那么 2+22/0 都不会被求值。

对于 all:
- 一旦找到第一个 False,即停止计算。
- 如果 False 是第一个元素,那么 2/0 不会被求值。

修复后的代码:

# 生成器表达式短路示例
print(any(x for x in [True, 2+2, False, 2/0]))  # 不会触发错误,输出 True
print(all(x for x in [True, 2+2, False, 2/0]))  # 不会触发错误,输出 False

总结

  • 如果希望利用短路,使用生成器表达式而不是列表。
  • 列表在传递前会先求值,生成器是惰性求值,可以避免不必要的计算。
2024-11-19