Python的any/all的短路行为是明确的吗?

58 投票
4 回答
11227 浏览
提问于 2025-04-17 14:57

这个讨论是受到这里内容的启发。

文档建议了一些与allany行为相同的代码。

那么,这些等价代码的行为应该算作它们的定义的一部分吗?还是说实现这些功能的时候可以不使用短路方式?

以下是来自cpython/Lib/test/test_builtin.py的相关摘录:

def test_all(self):
    self.assertEqual(all([2, 4, 6]), True)
    self.assertEqual(all([2, None, 6]), False)
    self.assertRaises(RuntimeError, all, [2, TestFailingBool(), 6])
    self.assertRaises(RuntimeError, all, TestFailingIter())
    self.assertRaises(TypeError, all, 10)               # Non-iterable
    self.assertRaises(TypeError, all)                   # No args
    self.assertRaises(TypeError, all, [2, 4, 6], [])    # Too many args
    self.assertEqual(all([]), True)                     # Empty iterator
    S = [50, 60]
    self.assertEqual(all(x > 42 for x in S), True)
    S = [50, 40, 60]
    self.assertEqual(all(x > 42 for x in S), False)

def test_any(self):
    self.assertEqual(any([None, None, None]), False)
    self.assertEqual(any([None, 4, None]), True)
    self.assertRaises(RuntimeError, any, [None, TestFailingBool(), 6])
    self.assertRaises(RuntimeError, all, TestFailingIter())
    self.assertRaises(TypeError, any, 10)               # Non-iterable
    self.assertRaises(TypeError, any)                   # No args
    self.assertRaises(TypeError, any, [2, 4, 6], [])    # Too many args
    self.assertEqual(any([]), False)                    # Empty iterator
    S = [40, 60, 30]
    self.assertEqual(any(x > 42 for x in S), True)
    S = [10, 20, 30]
    self.assertEqual(any(x > 42 for x in S), False)

这段代码并没有强制要求使用短路行为。

4 个回答

14

注意,这里并没有回答提问者的那个完全不同的问题。

如果你在这里想知道为什么 anyall 的调用看起来似乎没有短路效果,

一个原因是错误的期待:在调用中使用列表推导式,并且期待这个列表的构建能够短路:

>>> def print_and_return_num(num):
    print_and_return_num(num)
    return num
...

>>> any(print_and_return_num(num) for num in [1, 2, 3, 4])
1
True

>>> any([print_and_return_num(num) for num in [1, 2, 3, 4]])
1
2
3
4
True

在第二个例子中,列表推导式会先被计算出来:整个列表会在 any() 查看之前就被构建好。这是预期的行为,但可能需要一点时间才能看明白。

17

文档上说:

“如果可迭代对象中的任何一个元素为真,就返回True。如果可迭代对象是空的,就返回False。这和下面的代码是等价的:” (强调是我加的)...

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

如果any函数不进行短路操作,那它就不会和上面提到的代码等价,因为上面的代码显然是进行短路的。比如说,你可能会消耗掉生成器中比你想要的更多的元素。基于这个原因,我认为短路操作是有保证的

对于all函数,同样的道理也适用。

78

这个行为是有保证的。我提交了一个补丁,这个补丁最近被接受并合并了,所以如果你获取最新的源代码,你会看到短路行为现在是明确执行的。

git clone https://github.com/python/cpython.git
grep Short-circuit cpython/Lib/test/test_builtin.py

撰写回答