¿Es explícito el comportamiento de cortocircuito de Python?

Impulsado por la discusión aquí.

Los documentos sugieren algún código equivalente para el comportamiento de all y any

¿Debería el comportamiento del código equivalente ser considerado parte de la definición, o puede una implementación implementarlos de una manera que no sea un cortocircuito?

Aquí está el extracto relevante de 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) 

No hace nada para imponer el comportamiento del cortocircuito.

El comportamiento está garantizado . He contribuido con un parche , que se aceptó y fusionó recientemente, por lo que si toma las últimas fonts verá que el comportamiento de cortocircuito ahora se aplica explícitamente.

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

Los docs dicen

“Devolver verdadero si cualquier elemento del iterable es verdadero. Si el iterable está vacío, devuelva falso. EQUIVALENTE A: ” (énfasis mío) …

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

Si any no fuera un cortocircuito, no sería EQUIVALENTE al código publicado ya que el código publicado claramente es un cortocircuito. Podrías consumir más de un generador de lo que quieres, por ejemplo. A la luz de eso, digo que el comportamiento de cortocircuito está garantizado .

El mismo argumento podría ser hecho para all .

Tiene que cortocircuitarse, ya que se le puede dar un iterable no enlazado. Si no fuera cortocircuito, esto nunca terminaría:

 any(x == 10 for x in itertools.count()) 

A gotcha: asegúrate de no crear una lista al realizar la iteración; primero se comstackrá completamente

 def print_hi(item): print 'hi' return True >>> print any(print_hi(num) for num in [1, 2, 3, 4]) hi True >>> print any([print_hi(num) for num in [1, 2, 3, 4]]) hi hi hi hi True