Condicionales en Python: mucho más que if else

Condicionales en Python: Mucho más que if else

Cuando nos hablan de condicionales sin duda todos pensamos en las estructuras de control del tipo if – then – else. Y es evidente, porque este tipo de construcciones son las principales para generar ramas alternativas en cualquier lenguaje. Pero Python va mucho más allá de esto e integra las condicionales de maneras muy diversas. En este artículo te cuento todo lo que necesitas saber sobre las condicionales y el uso de condiciones en Python.

En Python se usan las condicionales en la forma if cond1 elif cond2 else. La condición es implícita en bucles for. Se puede encadenar comparaciones como a < b != c, comprobar alguna o todas las condiciones de un conjunto con any y all, comprobar la pertenencia con in y filtrar elementos con filter.

A continuación vamos a ir tocando uno a uno diferentes aspectos, como los del resumen que acabas de leer, con la idea de que tengas una visión general de este tema fundamental. Además, te ilustro todo esto con muchos ejemplos de condicionales para que no te queden dudas y para que puedas replicar si lo necesitas.

Qué son las condicionales, las condiciones, las comparaciones y los valores y operadores booleanos

Antes de empezar creo que hay que aclarar estos conceptos. Si ya los tienes perfectamente claros puedes saltar al siguiente apartado, si no, quédate para poder entender a la perfección el resto del artículo.

Quiero aclararlos porque a veces se usan estos términos indistintamente y son cosas diferentes aunque estén muy relacionadas entre sí.

Empezamos con lo más básico, los valores booleanos, llamados así por el álgebra inventada por el matemático inglés George Boole. El conjunto de valores booleanos se reduce a dos, que en Python se llaman True y False, y que representan los conceptos verdadero y false respectivamente. Estos valores pueden ser asignados a variables que pueden ser utilizadas de manera normal. Son los dos únicos valores válidos del tipo bool.

Asociado a estos valores nos topamos ahora con los operadores booleanos. Son operadores que nos permiten describir operaciones o expresiones booleanas con los valores booleanos. El resultado de estas operaciones booleanas es un valor booleano.

Los operadores booleanos de Python son los siguientes:

  • Operador not: se encarga de negar un valor y, por tanto, no permite obtener el opuesto, de forma que not False será True y not True será False.
  • Operador or: Nos permite obtener True si alguno de los operandos es True, en caso contrario obtendremos False. Por ejemplo, True or False or True será True mientras que False or False será False.
  • Operador and: Obtendremos True si y solo si todos los operandos son True. Por ejemplo, True and True es True, pero True and True and False será False.

Veamos algún ejemplo resuelto en la consola de Python, donde, como puedes ver, podemos utilizar paréntesis para agrupar subexpresiones dentro de otras:

>>> not True
False
>>> not False
True
>>> not not False
False

>>> False or True
True
>>> False or False or False
False
>>> False or False or not False
True
>>> not (False or True)
False

>>> True and True
True
>>> True and True and False
False
>>> True and not False
True
>>> not (False and not True)
True

>>> not (False or (True and not False))
False

Es importante darse cuenta de que, al igual que otros operadores, los operadores booleanos tienen preferencia unos sobre otros a la hora de evluar las expresiones. Más adelante en este artículo te explico esto.

Con los conceptos de valores, operadores y operaciones booleanos claros pasamos al siguiente nivel.

Existen expresiones en Python (y en casi todos los lenguajes de programación) cuyos operandos no son valores booleanos. Las principales expresiones de este tipo son las comparaciones de valores. Nos permiten comparar valores entre sí para comprobar si se cumple una determinada condición. Por ejemplo, queremos comprobar si un número es mayor que otro. Esto es una comparación que utiliza un comparador (>), dos operandos que son numéricos y el resultado que obtenemos es un valor booleano.

Los principales comparadores (aunque existe alguno más que veremos más adelante), también llamados operadores relacionales, son los siguientes:

  • Comparador de igualdad ==. Permite comprobar si dos valores o el contenido de dos variables es el mismo.
  • Comparador de no igualdad !=. Para comprobar si dos valores son distintos.
  • Comparador de mayor >. Nos permite conocer si un valor es mayor que otro.
  • Comparador de mayor o igual >=. Nos permite conocer si un valor es mayor o igual que otro.
  • Comparador de menor <. Nos permite conocer si un valor es menor que otro.
  • Comparador de mayor o igual >=. Nos permite conocer si un valor es mayor o igual que otro.
  • Comparador de identidad is. Nos sirve para comprobar si dos variables hacen referencia al mismo objeto.

Podemos comprar valores numéricos enteros, valores reales, caracteres, cadenas de texto, listas, etc., pero ten en cuenta que el comparador puede funcionar de manera diferente en función del tipo de datos que estemos comparando.

Vemos algunos ejemplos:

>>> 0 == 0
True
>>> 0 == 1
False
>>> 'hola' == 'hola'
True
>>> 'Código Pitón' == 'código pitón'
False

>>> 3 > 1
True
>>> 3 > 3
False
>>> 1 > 3
False
>>> 3 >= 1
True
>>> 3 >= 3
True
>>> 3 >= 1
True

>>> 1 < 3
True
>>> 1 <= 3
True
>>> 1 <= 1
True

Cualquier expresión que devuelva un valor booleano puede ser considerado como una condición. Decimos que una condición se cumple si el valor booleano asociado es True y que no se cumple si es False.

Una vez que tenemos una condición podemos crear un condicional. Un condicional o, mejor dicho, una sentencia o estructura condicional, es un mecanismo que nos permite decidir si ejecutamos un determinado bloque o fragmento de código o no, ya sea una sola vez (condicional simple) o varias veces (bucle o ciclo).

Las condicionales nos permiten por tanto, crear distintas ramas de código que se ejecutarán o no en función de distintas condiciones que definamos. Además, nos permiten decidir si ejecutamos un fragmento de código de manera repetida un número concreto de veces, es decir, un bucle.

Y este es un brevísimo resumen de estos conceptos. Vamos ahora a profundizar un poco más en todo lo que tiene que ver con las condicionales en Python, pero lo vamos a hacer paso a paso.

Condicionales clásicas if

En Python las condicionales clásicas permiten crear una rama opcional (if), dos ramas (if else) con una única condición, o más ramas (if elif else) con dos o más condiciones. Además permiten determinar el número de iteraciones que realiza un bucle for, cuya condición es implícita, o un bucle while.

Condicionales if else

Nos encontramos ante el tipo de condicional más fundamental y básico. Se trata de ejecutar un fragmento de código si se cumple una condición dada y, si no se cumple dicha condición, no se ejecuta el fragmento. Para ello utilizamos la sentencia if con el formato del siguiente ejemplo:

valor = 10
if valor > 5:
    print('Se cumple la condición')

Fíjate en dos cosas muy importantes. La primera es que tras la condición debemos poner dos puntos (:). La segunda es que el bloque de código que queremos que se ejecute debe estar correctamente indentado o sangrado, es decir las líneas de código deben empezar más a la derecha que la línea de if. En Python se define un tamaño de tabulación de cuatro espacios.

Al ejecutarse el código anterior, como la condición es cierta, se nos mostrará por pantalla el mensaje «Se cumple la condición«.

Si queremos que se ejecute un código alternativo en caso de que la condición no se cumpla, es decir, de que sea False, podemos crear una nueva rama alternativa mediante la sentencia else de la siguiente manera:

valor = 10
if valor > 15:
    print('Se cumple la condición')
else:
   print('No se cumple la condición')

Fíjate que la palabra else se escribe al mismo nivel (en vertical) que el if y el código de la nueva rama también debe escribirse indentado. La ejecución del código anterior nos mostrará por pantalla el mensaje «No se cumple la condición» únicamente.

Condicionales if elif else

Si necesitamos hacer más de dos ramas alternativas podemos escribirlas encadenando sentencias elif, que es una contracción de else e if, y proporcionando distintas condiciones de la siguiente manera:

valor = 9
if valor < 5:
    print('Es menor que 5')
elif valor < 10:
    print('Es menor que 10')
else:
    print('Es mayor o igual que 10')

Este código imprimirá por pantalla, como ya te imaginas, el mensaje «Es menor que 10» y nada más.

Esta estructura nos permite ejecutar una única rama de todas las definidas, es decir, que son excluyentes entre sí.

Estructura condicional switch case

En Python no existe una estructura condicional del estilo switch case. Si necesitas algo parecido, te recomiendo el siguiente artículo donde te explico con todo lujo de detalles cómo hacer switch en Python. Este artículo te permite, además, profundizar un poco más en el tema de condicionales en Python.

Condicionales de bucle

Como te dije antes, las condiciones también sirven para controlar el número de iteraciones de un bucle. Esto quiere decir que siempre que tengamos un bucle también tenemos una condicional, con la diferencia de que el código del cuerpo del bucle se puede ejecutar más de una vez. Veamos los dos tipos de bucles disponibles en Python.

Bucle while

El bucle while (mientras) de Python es el bucle típico de casi cualquier lenguaje de programación. Permite repetir un fragmento de código una y otra vez mientras se cumpla una condición dada. El fragmento de código que se repite en un bucle es lo que se conoce como cuerpo del bucle.

La sintaxis es muy sencilla como podemos ver en el siguiente ejemplo y sigue unas reglas parecidas a las explicadas previamente para el if:

valor = 0
while (valor < 5):
    print(f'Valor es {valor}')
    valor += 1 #nos aseguramos dentro del bucle de que en algún momento la condición se haga falsa
 

Este código genera el siguiente resultado:

Valor es 0
Valor es 1
Valor es 2
Valor es 3
Valor es 4

Fíjate que basta con poner una condición que sea cierta y que en algún momento se haga falsa para detener el bucle. Recuerda que un buen truco para entender este bucle es pensar el código se repite mientras la condición sea cierta.

Si antes de comenzar el bucle la condición es falsa no se ejecutará el cuerpo del bucle ninguna vez.

Bucle for

El bucle for en Python es muy diferente de los bucles for que nos encontramos habitualmente en otros lenguajes de programación. No es el objetivo de este artículo ahondar en esto, pero sí es interesante mencionarlo porque la condición de este bucle es implícita, no hace falta indicarla.

Normalmente se utiliza para iterar por todos y cada uno de los elementos de una secuencia, como puede ser una lista, una tupla, un diccionario, range, cadenas de texto, etc.

De esta manera, la condición implícita de un bucle for nos permite ejecutar un código tantas veces como elementos tenga la secuencia. Además, nos proporciona cada uno de esos elementos en una variable. Lo vemos en un ejemplo:

lista = [1, 2, 3]

for elemento in lista:
    print(elemento)

Esto muestra el siguiente resultado:

1
2
3

Fíjate que el cuerpo del bucle se ejecuta tantas veces como elementos tiene la lista. Además, en cada vuelta del bucle tenemos en la variable elemento el valor de cada uno de los elementos de la lista.

La ventaja de este tipo de bucles es que nos podemos olvidar de utilizar índices y, por tanto, de escribir condiciones que siempre tienden a ser iguales al hacer este tipo de recorridos.

Bucles con rama else

Una cosa ciertamente extraña, y que no conoce mucha gente, de los bucles en Python es que permiten añadirles una rama else. En dicha rama podemos escribir un fragmento de código que se ejecuta cuando la condición del bucle (ya sea implícita o explícita) se hace falsa, eso sí, siempre y cuando el bucle no haya terminado a causa de una sentencia break.

Un break es una instrucción de detiene de manera inmediata la ejecución del bucle, aunque la condición del mismo sea True, y permite al resto del código posterior al bucle continuar con su ejecución.

La sentencia else se puede utilizar tanto en bucles for como en bucles while. Veamos un ejemplo:

lista = [1, 2, 3]
for elemento in lista:
    print(elemento)
else:
    print('Hemos llegado al final del bucle')

El resultado del código anterior será el siguiente:

1
2
3
Hemos llegado al final del bucle

Tienes que darte cuenta de que el mensaje final, que se imprime en la rama else del bucle se muestra por pantalla porque el bucle ha terminado sin un break. Veamos a continuación este caso:

lista = [1, 2, 3]
for elemento in lista:
    print(elemento)
    if elemento == 3:
        break
else:
    print('Hemos llegado al final del bucle')

En este caso, el bucle termina a causa del break, por lo que el print con el mensaje final no se ejecuta, pues no hemos dado tiempo a que se reevalúe la condición para comprobar que es falsa. Por tanto, el resultado es el siguiente:

1
2
3

De la misma manera funciona para un bucle while. Vemos un pequeño ejemplo:

n = 10
while n < 20:
    print(n)
    n += 1
    if n % 7 == 0:
        break
else:
    print('Hemos llegado al final del bucle') 

¿Qué crees que mostrará por pantalla el código anterior? Dejo que lo averigües por ti mismo 😉

Puede costar encontrar uso a esta característica de Python, sobre todo si no la comprendes muy bien, pero una vez que lo haces, es fácil ver su utilidad. Un posible uso habitual sucede cuando necesitamos distinguir si un bucle ha terminado porque se cumple una situación que se quería alcanzar o si termina por el caso contrario. Por ejemplo, buscar un elemento en una lista. Al buscar un elemento en una lista de manera secuencial podemos salir del bucle al encontrar el elemento o al terminar de recorrer la lista sin haberlo encontrado. Si usamos un else en el bucle no tenemos la necesidad de utilizar una variable extra como indicador de qué ha sucedido.

Expresiones condicionales: el operador ternario

Otra manera de realizar condicionales en Python es por medio del uso del famoso operador ternario. El operador ternario nos permite decidir entre dos valores o dos sentencias sencillas en función de una condición.

La sintaxis es: opción_true if condición else opción_false, donde opción_true es el valor que se toma o la sentencia sencilla que se ejecuta si se cumple condición. Si no se cumple, se toma opción_false.

Esto nos permite realizar cosas como la siguientes:

>>> valor = 10
>>> print('Es menor' if valor < 20 else 'Es mayor')
Es menor
>>> 
>>> print('Es menor' if valor < 5 else 'Es mayor')
Es mayor
>>> 
>>> x, y = 5, 10
>>> z = x if x < y else y
>>> z
5
>>> 
>>> def funcion_1():
...     print(1)
... 
>>> def funcion_2():
...     print(2)
... 
>>> funcion_1() if valor > 0 else funcion_2()
1

Ya ves que podemos usarlo tanto para invocar funciones (o hacer otras operaciones) como para seleccionar valores para una asignación o para pasar como parámetro a una función.

Funciones basadas en condiciones

Es muy interesante la posibilidad de poder definir funciones basada en condiciones, es decir, funciones que alteren su comportamiento según una condición que se puede aplicar a determinados datos. Para ello necesitamos un mecanismo para poder enviar una condición a una función para que esta pueda utilizarla.

Lamentablemente no podemos crear una condición sin tener los datos antes, lo que sí podemos hacer es encapsular en una función auxiliar dicha condición, que devolverá True si se cumple la condición y False en caso contrario. Dicho de otra manera, crear una función de este estilo es como crear una condición parametrizada. Las funciones, al ser objetos de primer orden, pueden ser proporcionadas a otras funciones como parámetros.

Por ejemplo, vamos a crear una condición parametrizada para poder evaluar si un número es par. Lo podemos hacer de la siguiente manera:

def es_par(valor):
   return not valor % 2

Fíjate en que la función devuelve el resultado de hacer not no a un valor booleano, sino a un número entero. Esto es posible porque Python evalúa cualquier número como True excepto el 0, que es evaluado como False. Porfundizo en este aspecto más adelante en este artículo. Sigue leyendo. 🙂

Ahora podríamos pasar esta función como parámetro a otra para que la utilice. Por ejemplo, queremos hacer una función que nos diga si algún elemento de una lista cumple una determinada condición:

def alguno_cumple(condicion, lista):
    for elemento in lista:
        if condicion(elemento):
            return True
    return False

De esta manera podemos, por ejemplo, comprobar si alguno de los elementos de una lista de números enteros cumple la condición de ser par:

>>> alguno_cumple(es_par, [1, 2, 3])
True
>>> alguno_cumple(es_par, [1, 5, 3])
False

Fíjate que la función de la condición, nuestra condición parametrizada, se pasa como parámetro a la función alguno_cumple sin poner paréntesis, pues queremos proporcionar la referencia a dicha función y no el resultado de ejecutarla.

Ahora imagina la potencia y la flexibilidad que te da el poder hacer esto.

De hecho, Python tiene una función muy interesante que se basa en esta idea y que nos permite filtrar listas. El filtrado de listas es muy útil en diversas situación y consiste en eliminar de una lista aquellos elementos no deseados. Esta función es la función filter.

La función filter recibe una función que evalúa una condición recibida por parámetro, es decir, una condición parametrizada, y un iterable, por ejemplo, una lista, y nos devuelve un iterador con el que podemos recorrer aquellos elementos del iterable de origen que cumplen la condición proporcionada. Con la función list podemos convertir el iterador a una lista.

Veamos un ejemplo donde elimino de una lista aquellos números que no sean pares utilizando nuestra función, creada anteriormente, es_par:

>>> list(filter(es_par, [1, 2, 3, 4, 5, 6, 7]))
[2, 4, 6]

Es importante comprender que dentro de la función filter se va invocando de manera sucesiva a la función que encapsula la condición, por tanto, esta última tiene que poder recibir un parámetro del tipo contenido en el iterable proporcionado.

A veces, las condiciones son lo suficientemente sencillas como para no necesitar encapsularlas en una función. Para ello podemos hacer uso de expresiones lambda de la siguiente manera. Vamos a filtrar ahora aquellos elementos que sean pares para quedarnos con los impares:

>>> list(filter(lambda x: x % 2, [11, 12, 13, 14, 15]))
[11, 13, 15]

Interesante, cómodo y potente, ¿no crees?

Comparaciones avanzadas

Vamos a seguir ahora hablando de comparaciones y profundizando un poco en ellas. Es evidente que cuando pensamos en realizar comparaciones de mayor o menor nos vienen a la cabeza los números, pero Python permite comprar entre sí otros tipos de datos que no son numéricos.

Podemos comparar cadenas de texto. La comparación de igualdad ya la vimos más arriba, pero también podemos realizar comparaciones de mayor o menor con cadenas de texto, teniendo en cuenta que, una cadena es menor que otra si se encuentra antes al ordenar por orden alfabético como se haría en un diccionario. Ejemplos:

>>> 'a' < 'b'
True
>>> 'b' > 'z'
False
>>> 'asado' < 'carne'
True
>>> 'texto' <= 'texto'
True
>>> 'hola' != 'HOLA'
True
>>> 'A' < 'a'
True

¡Cuidado! Fíjate que una letra mayúscula siempre es menor que su correspondiente en minúscula. Esto sucede porque las letras mayúsculas se encuentran antes en la tabla de caracteres que las minúsculas.

Otro tipo de comparaciones similares son aquellas que podemos hacer entre dos listas o tuplas. La comparación de igualdad es cierta cuando las dos listas son iguales elemento a elemento, es decir, que deben ser de la misma longitud y tener los mismos elementos y en el mismo orden:

>>> [1, 2, 3, 4] == [1, 2, 3, 4]
True
>>> [10, 20, 30] == [20, 10, 30]
False
>>> [10, 20, 30] != [20, 10, 30]
True

En el caso del menor o mayor que, la comparación cambia un poco y se asemeja a la comparación entre cadenas de texto. Primero se compara el primer elemento de una secuencia con el primero de la otra. Si la primera comparación se cumple entonces se cumple la comparación para toda la lista. Si la primera comparación no se cumple, entonces no se cumple la comparación para toda la lista. Si los elementos son iguales, entonces se pasa a comparar entre sí los elementos en la segunda posición. Este algoritmo se repite hasta que se acaben alguna de las dos listas. Veamos algunos ejemplos:

>>> [1] > [0, 2]
True
>>> [0] > [0, 1]
False
>>> [1, 2, 3] < [2, 2, 3]
True
>>> [1, 2, 3] < [1, 3, 3]
True
>>> [1, 0, 0] > [0, 128, 256]
True
>>> [1, 2, 3, 4] < [1, 2, 3]
False
>>> [1, 2, 3, 4] > [1, 2, 3]
True

Fíjate en el último caso. La segunda lista se acaba antes que la primera, pero hasta el tercer elemento ambas listas son iguales. Como la primera tiene más elementos que la segunda también puede considerarse mayor.

¡Ojo! Las comparaciones entre arrays de NumPy o DataFrames de pandas no devuelven booleanos como te acabo de mostrar. Acude a la documentación de dichas librerías si necesitas usar esa característica: documentación de NumPy y documentación de pandas.

Otra posible comparación se da entre conjuntos o sets. Dos conjuntos son iguales si tienen exactamente los mismos elementos. Un conjunto A es mayor que otro conjunto B si A es un superconjunto propio de B, es decir, si es un superconjunto y además A y B no son iguales. Ejemplo:

>>> {1, 2, 3} == {3, 1, 2}
True
>>> {1, 2, 3} == {4, 1, 2}
False
>>> {1, 2, 3} > {1, 2}
True
>>> {3} < {1, 2, 3}
True
>>> {1, 2, 3} > {1, 2, 3}
False

Operador de pertenencia in

Un operador muy utilizado es el de pertenecia de un elemento a una secuencia. Para aplicarlo se utiliza la palabra reservada in, que puede ser combinada con el operador booleano de negación not para comprobar si un elemento no se encuentra en una secuencia.

Veamos algunos ejemplo:

>>> 2 in range(5)
True
>>> 2 in [1, 2, 3]
True
>>> 4 in (2, 4, 6)
True
>>> 5 in {5, 15}
True
>>> 5 in [1, 2, 3]
False
>>> 5 not in [1, 2, 3]
True
>>> 3 in {1: 'uno', 3: 'tres', 5: 'cinco'}
True
>>> 'uno' in {1: 'uno', 3: 'tres', 5: 'cinco'}
False
>>> 3 in range(5)
True

Fíjate que podemos usar el operador in con diccionarios. No obstante nos sirve para comprobar si una clave se encuentra en el diccionario. Si queremos comprobar si un valor se encuentra en el diccionario podemos utilizar el método values del diccionario de la siguiente manera:

>>> diccionario = {1: 'uno', 3: 'tres', 5: 'cinco'}
>>> 'uno' in diccionario.values()
True

También podemos utilizar el operador in para comprobar si un carácter o una cadena de texto se encuentra en otra cadena:

>>> 'a' in 'abc'
True
>>> 'ab' in 'abc'
True
>>> 'bc' in 'abc'
True
>>> 'cd' in 'abc'
False

Para más detalles sobre subcadenas o substrings en Python lee este artículo.

Múltiples comparaciones y condiciones complejas

Suele suceder que las condiciones que tenemos que utilizar son más complejas y más amplias en general que las que te presento en los ejemplos de este artículo. Veamos algunas cosas que hay que considerar a la hora de crear estas condiciones y algunos consejos que harán tu vida más fácil.

Preferencia de operadores booleanos: agrupando condiciones

Es importante conocer que, como todos los operadores, los operadores booleanos not, and y or tienen un orden de preferencia o de precedencia a la hora de evaluar las expresiones. Esto es fundamental para poder construir correctamente nuestras condiciones.

El operador de mayor preferencia es not. Le sigue and y, finalmente, or. Esto implica que a la hora de evaluar una expresión booleana se aplicarán los operados en ese orden.

Por ejemplo, en la expresión A and not B or C se evaluará primero not B (llamemos D al resultado), después A and D (llamemos E al resultado) y finalmente D or C.

Como siempre, podemos usar paréntesis para agrupar subexpresiones y darles prioridad para que se resuelvan antes. Te cuento en este artículo este y otros usos de los paréntesis, corchetes y llaves en Python.

Si quisieramos que se evaluara antes el or que el and podríamos escribir la condición de la siguiente manera: A and (not B or C), o tal vez, lo que queremos negar sea el resultado del or y no B, así que podemos escribir A and not (B or C).

Te pongo algunos ejemplos para que analices su resultado:

>>> True and True or False
True
>>> True and False or True
True
>>> not False and True
True
>>> not (False and True)
True
>>> not False or True
True
>>> not (False or True)
False
>>> True and (not (True or False) or not False)
True

Evaluación en cortocircuito

Otro comportamiento importante de Python que hay que tener en cuenta a la hora de escribir condiciones es la evaluación en cortocircuito. ¿Que qué es eso? Muy sencillo.

La evaluación en cortocircuito es un mecanismo por el cual una expresión booleana se evalúa de izquierda a derecha, teniendo en cuenta la prioridad de los operadores, y deteniendo la evaluación en cuanto puede determinarse el valor sin necesidad de evaluar el resto de la expresión.

Esto se entiende mejor con un par de ejemplos. El más sencillo que podemos poner es A or B. En esta expresión, si sabemos que A es True no es necesario considerar B ni evaluar el resultado de la expresión completa, ya que siempre será True, tenga B el valor que tenga.

Lo mismo sucede con A and B si A es False, ya que toda la expresión será False. La ventaja se aprecia en expresiones más complejas, por ejemplo, A and B or not (C or (not D and E)). Si te fijas, si A y B son True, ya no hace falta evaluar el resto de la expresión pues será True.

¿Por qué es esto importante a la hora de escribir las condiciones? Porque así es posible incorporar en una expresión booleana una operación que en determinados casos puede dar error, por ejemplo, una división por cero, si sabemos que no va a llegar a evaluarse gracias a la evaluación en cortocircuito.

Te lo ilustro con un ejemplo. Imagina que tenemos que repartir un número de objetos n entre un conjunto de p personas. Es posible que el conjunto de personas sea un conjunto vacío, es decir, que no haya personas. Queremos saber si a cada persona le tocan más de m objetos. Para ello podemos escribir la condición de manera sencilla como sigue:

>>> o = 10
>>> p = 3
>>> m = 2
>>> p != 0 and o / p > m
True

En este caso, como 10 / 3 es mayor que 2 la condición es True. No hay peligro de que nuestro código se rompa si p vale 0 porque es seguro gracias a la evaluación en cortocircuito. Si p es 0 la primera subcondición p != 0 se evalua como False y por tanto deja de evaluarse el resto de la expresión, lo que hace que nunca se ejecute la división por cero. Además nuestra condición es False representando la situación de que a ninguna persona le tocan más de m objetos ya que no hay personas ni división (si es que es este el resultado que se busca):

>>> o = 10
>>> p = 0
>>> m = 2
>>> p != 0 and o / p > m
False

También es muy importante, si tu programa realiza cálculos de manera intensiva, que tengas en cuenta este tipo de evaluación con el fin de optimizar tu código y reducir su complejidad temporal.

Comparaciones en cadena

Otra posibilidad muy interesante y cómoda es poder encadenar comparaciones todas las veces que queramos. Así, podemos hacer cosas como a > b > c. O incluso a > b < c != d >= e.

Para que una comparación en cadena sea True deben cumplirse todas y cada una de las comparaciones individuales. En la expresión a > b < c != d, tiene que cumplirse que a > b and b < c and c != d.

Como ves, es una manera abreviada de escribir condiciones porque nos permite ahorrarnos el escribir cada comparación de un par de valores de manera individual así como los correspondientes operadores booleanos.

Veamos algunos ejemplos de comparaciones encadenadas, donde también se pueden usar los operadores in e is:

>>> 1 < 2 < 3
True
>>> 1 < 2 > 0
True
>>> 10 > 2 != 3 < 5 == 5
True
>>> 10 > 8 > 6 > 4 > 5
False
>>> 'f' in 'cafe' in 'cafeteria' not in 'restaurante'
True
>>> texto = 'codigopiton.com'
>>> saludo ='hola'
>>> texto is not saludo not in 'adios'
True

Ten en cuenta que las comparaciones en cadena también se evalúan en cortocircuito.

Múltiples condiciones: funciones any y all

Python proporciona una par de funciones de conveniencia, que son any y all, que nos permiten evaluar si se cumple alguna o se cumplen todas, respectivamente, de las expresiones booleanas contenidas en un iterable, como por ejemplo una lista.

La función any es equivalente a tener una expresión booleana donde se utiliza or entre cada uno de los términos, del estilo A or B or C or D. Podríamos escribir esa expresión de la siguiente manera, utilizando una tupla, any((A, B, C, D)), que puede resultar muy conveniente en expresiones largas. Ejemplos:

>>> 1 > 3 or 10 > 15 or 3 > 2 or 5 == 4
True
>>> any((1 > 3, 10 > 15, 3 > 2, 5 == 4))
True

Por su parte, la función all hace lo mismo, pero nos sirve para evaluar la expresión utilizando and entre cada término. Así, A and B and C and D se puede escribir como all((A, B, C, D)):

>>> 10 > 5 and 40 > 20 and 10 <= 15
True
>>> all((10 > 5, 40 > 20, 10 <= 15))
True
>>> all((1 < 2, 2 < 3, 3 < 4, 4 < 5, 7 < 6))
False

Valores booleanos True y False: más amplio de lo que parece

En Python podemos encontrar los valores True y False más allá de los propios valores True y False. Por ejemplo, y como sucede en lenguajes como C, el valor entero 0 se corresponde con False mientras que cualquier otro número se corresponde con True (incluidos NaN e infinito). Podemos por tanto utilizar números (o expresiones o funciones cuyo resultado sean números) como condiciones:

>>> if 0:
...     print('Esto no va a mostrarse en pantalla')
... 
>>> if 1:
...     print('Esto sí que va a mostrarse')
... 
Esto sí que va a mostrarse
>>> if 2:
...     print('Esto también se muestra')
... 
Esto también se muestra

Podemos utilizar la clase bool, cuyo constructor recibe un valor de cualquier tipo para generar un objeto que sea True o False. Asi, podemos ver que:

>>> bool(0)
False
>>> bool(1)
True
>>> bool(2)
True
>>> bool(5.5)
True
>>> bool(0.0)
False
>>> import math
>>> bool(math.nan)
True
>>> bool(math.inf)
True

Pero cuidado, porque aunque los números diferentes de 0 se consideren como True en una condición, no son iguales a True. Es más no se debería comparar un valor booleano con un número entero, ya que son de tipos diferentes:

>>> 2 == False
False
>>> 5.5 == False
False

Esta regla tiene una excepción y es que los números 0 y 1 sí se pueden comparar directamente con booleanos ya que en Python se consideran como False y True respectivamente. Es más, los valores booleanos se consideran numéricos.

>>> 0 == False
True
>>> 0 == True
False
>>> 
>>> 1 == True
True
>>> 1 == False
False

Y precisamente porque estos valores se consideran numéricos se puede realizar operación matemáticas con ellos e incluso usarlos como índices para el acceso a posiciones en una lista, por ejemplo:

>>> True + True
2
>>> True + True == 2
True
>>> [-1, 1][False]
-1
>>> (-1, 1)[True]
1

Al igual que cualquier número se puede utilizar como una condición lo mismo sucede con las secuencias y en general con objetos a los que se puede aplicar la función len. Si estos son vacíos se consideran como False y si contienen algún elemento, True. Veamos algunos ejemplos con cadenas de texto, listas, tuplas y conjuntos:

>>> bool('')
False
>>> bool('texto')
True
>>> bool([])
False
>>> bool([1, 2, 3])
True
>>> bool(())
False
>>> bool((4, 5))
True
>>> bool({})
False
>>> bool({10, 20})
True

El valor especial None también se resuelve a False:

>>> bool(None)
False
>>> bool(not None)
True

Las referencias a funciones también se evalúan como True. Por esto hay que tener mucho cuidado al invocar a una función que debe devolver un valor booleano y acordarse de indicar los paréntesis, de lo contrario, siempre se evaluará como True aunque la función devuelva False:

>>> def falso():
...     return False
... 
>>> bool(falso())
False
>>> bool(falso)
True
>>> if falso:
...     print('¿Debe mostrarse este mensaje?')
... 
¿Debe mostrarse este mensaje?

Conclusión

Hemos aprendido que algo tan básico,como como las condiciones, y algo que parece tan insignificante, como los valores booleanos, conforman una gran mundo dentro del lenguaje Python. Conocer todos los aspectos que te expongo en este artículo no solo te llevará a escribir mejor código, sino que te evitará algunos problemas y efectos no deseados en tus programas, ya que te permitirá entender mejor qué es lo que sucede.

¡Y todavía faltan cosas por ver! Como por ejemplo, el poder definir si los valores de un tipo definido por nosotros se evalúan como False o True, las funciones sort y sorted para ordenar secuencias, ya que se usan comparaciones, la definición de comparadores para establecer relaciones arbitrarias entre los objetos de una clase, etc. En fin, que hay más cosas en las que profundizar, pero en algún momento hay que parar. Veremos todo esto en próximos artículos…

La Hoja de Referencia de Python – ¡Gratis!

La Hoja de Referencia de Python - Código Pitón
Consigue trucos, consejos y actualizaciones y, por supuesto, la Hoja de Referencia de Python gratis.



Antes de suscribirte consulta aquí la Información Básica sobre Protección de Datos. Responsable de los datos: Laura Otero Moreira. Finalidad de la recogida y tratamiento de los datos personales: enviarte boletín informativo de Python y comunicaciones comerciales. Legitimación: tu consentimiento. Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (MailRelay). Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control. Más información en nuestra política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal, incluida información sobre acceso, conservación, rectificación, eliminación, seguridad y otros temas.