¿Python admite cortocircuitos?

Respuestas:

192

El cortocircuito de la conducta en el operador and, or:

Primero definamos una función útil para determinar si algo se ejecuta o no. Una función simple que acepta un argumento, imprime un mensaje y devuelve la entrada, sin cambios.

>>> def fun(i):
...     print "executed"
...     return i
... 

Se puede observar la conducta de cortocircuito de Python de and, oroperadores en el siguiente ejemplo:

>>> fun(1)
executed
1
>>> 1 or fun(1)    # due to short-circuiting  "executed" not printed
1
>>> 1 and fun(1)   # fun(1) called and "executed" printed 
executed
1
>>> 0 and fun(1)   # due to short-circuiting  "executed" not printed 
0

Nota: El intérprete considera que los siguientes valores significan falso:

        False    None    0    ""    ()    []     {}

Comportamiento de cortocircuito en función: any(), all():

Python any()y sus all()funciones también admiten cortocircuitos. Como se muestra en los documentos; evalúan cada elemento de una secuencia en orden, hasta encontrar un resultado que permita una salida temprana de la evaluación. Considere los ejemplos a continuación para comprender ambos.

La función any()verifica si algún elemento es Verdadero. Deja de ejecutarse tan pronto como se encuentra un Verdadero y devuelve Verdadero.

>>> any(fun(i) for i in [1, 2, 3, 4])   # bool(1) = True
executed
True
>>> any(fun(i) for i in [0, 2, 3, 4])   
executed                               # bool(0) = False
executed                               # bool(2) = True
True
>>> any(fun(i) for i in [0, 0, 3, 4])
executed
executed
executed
True

La función all()verifica que todos los elementos sean Verdaderos y deja de ejecutarse tan pronto como se encuentre un Falso:

>>> all(fun(i) for i in [0, 0, 3, 4])
executed
False
>>> all(fun(i) for i in [1, 0, 3, 4])
executed
executed
False

Comportamiento de cortocircuito en la comparación encadenada:

Además, en Python

Las comparaciones pueden encadenarse arbitrariamente ; por ejemplo, x < y <= zes equivalente a x < y and y <= z, excepto que yse evalúa solo una vez (pero en ambos casos zno se evalúa en absoluto cuando x < yse determina que es falso).

>>> 5 > 6 > fun(3)    # same as:  5 > 6 and 6 > fun(3)
False                 # 5 > 6 is False so fun() not called and "executed" NOT printed
>>> 5 < 6 > fun(3)    # 5 < 6 is True 
executed              # fun(3) called and "executed" printed
True
>>> 4 <= 6 > fun(7)   # 4 <= 6 is True  
executed              # fun(3) called and "executed" printed
False
>>> 5 < fun(6) < 3    # only prints "executed" once
executed
False
>>> 5 < fun(6) and fun(6) < 3 # prints "executed" twice, because the second part executes it again
executed
executed
False

Editar:
Otro punto interesante a tener en cuenta : - Lógico and, losor operadores en Python devuelven el valor de un operando en lugar de un booleano ( Trueo False). Por ejemplo:

La operación x and yda el resultadoif x is false, then x, else y

A diferencia de otros lenguajes &&, por ejemplo , ||operadores en C que devuelven 0 o 1.

Ejemplos:

>>> 3 and 5    # Second operand evaluated and returned 
5                   
>>> 3  and ()
()
>>> () and 5   # Second operand NOT evaluated as first operand () is  false
()             # so first operand returned 

Del mismo modo, el oroperador devuelve el valor más a la izquierda para el cual bool(value)== Truemás a la derecha el valor más falso (según el comportamiento de cortocircuito), ejemplos:

>>> 2 or 5    # left most operand bool(2) == True
2    
>>> 0 or 5    # bool(0) == False and bool(5) == True
5
>>> 0 or ()
()

Entonces, ¿cómo es esto útil? Un ejemplo de uso dado en Practical Python por Magnus Lie Hetland:
Digamos que se supone que un usuario ingrese su nombre, pero puede optar por no ingresar nada, en cuyo caso desea usar el valor predeterminado '<unknown>'. Podría usar una declaración if, pero también podría indicar las cosas de manera muy sucinta:

In [171]: name = raw_input('Enter Name: ') or '<Unkown>'
Enter Name: 

In [172]: name
Out[172]: '<Unkown>'

En otras palabras, si el valor de retorno de raw_input es verdadero (no una cadena vacía), se le asigna un nombre (nada cambia); de lo contrario, '<unknown>'se asigna el valor predeterminado a name.

Grijesh Chauhan
fuente
1
Pequeñas objeciones: la lista explícita de valores falsos es ligeramente engañosa. Cualquier tipo puede tener uno o más valores falsos. Por convención, todos los tipos numéricos de valor 0son Falsy (lo que no es justo 0, es 0.0, 0j, decimal.Decimal(0), fractions.Fraction(0), etc.), como lo son todas las colecciones de longitud 0(por lo que en la parte superior de lo que en la lista, b''[AP3], u''[Py2] y set()/ frozenset()son todos incorporados que se evalúan como falsos), pero los tipos definidos por el usuario / de terceros pueden definir los suyos propios (con __bool__[Py3] / __nonzero__[Py2] directamente o indirectamente definiendo __len__).
ShadowRanger
@ShadowRanger aquí tu comentario completará mi respuesta. gracias por agregar esta nota
Grijesh Chauhan
Además, python evalúa doblemente los condicionales de cortocircuito, si luego se usan como booleanos ... a menos que estén en una declaración if, que está privilegiada: gist.github.com/earonesty/08e9cbe083a5e0583feb8a34cc538010
Erik Aronesty
48

Si. Pruebe lo siguiente en su intérprete de Python:

y

>>>False and 3/0
False
>>>True and 3/0
ZeroDivisionError: integer division or modulo by zero

o

>>>True or 3/0
True
>>>False or 3/0
ZeroDivisionError: integer division or modulo by zero
Caprooja
fuente