Python的any/all的短路行为是明确的吗?
这个讨论是受到这里内容的启发。
那么,这些等价代码的行为应该算作它们的定义的一部分吗?还是说实现这些功能的时候可以不使用短路方式?
以下是来自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
注意,这里并没有回答提问者的那个完全不同的问题。
如果你在这里想知道为什么 any
和 all
的调用看起来似乎没有短路效果,
一个原因是错误的期待:在调用中使用列表推导式,并且期待这个列表的构建能够短路:
>>> 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
函数,同样的道理也适用。