Control de flujo y comprensiones

El for de Python no cuenta números, recorre cosas, y eso cambia cómo se escribe todo. Esta lección va de eso, de las comprensiones —que sustituyen a la mitad de los bucles— y de match, que compara formas en vez de valores.

El for de Python no cuenta, recorre

for i, fruta in enumerate(["pera","uva","kiwi"], start=1):
    print(f"{i}. {fruta}")

for nombre, precio in zip(nombres, precios):
    ...

list(range(2, 10, 3))     # [2, 5, 8]
salida1. pera
2. uva
3. kiwi
range(2,10,3) -> [2, 5, 8]

No hay un for (i = 0; i < n; i++). El for de Python recorre lo que le des: una lista, una cadena, un diccionario, un archivo, un generador. Si necesitas el índice, enumerate; si necesitas recorrer dos cosas a la vez, zip. Escribir for i in range(len(lista)) es la señal de que te falta una de las dos.

El else de los bucles

for n in [4, 6, 9]:
    if n % 2 == 1:
        print(f"encontré un impar: {n}")
        break
else:
    print("no había ninguno")      # solo si NO hubo break
salidaencontré un impar: 9

Es la rareza más conocida del lenguaje, y casi nadie la usa. El else de un for se ejecuta cuando el bucle termina sin pasar por un break. Sirve exactamente para el patrón «busca algo, y si no lo encuentras haz esto otro», que si no obliga a una variable de bandera.

Comprensiones

[n*n for n in nums if n % 2 == 0]       # lista
{n: n*n for n in range(4)}              # diccionario
{n % 3 for n in nums}                   # conjunto
(n*n for n in nums)                     # generador: no calcula nada todavía
salidacuadrados pares: [0, 4, 16, 36, 64]
dict:            {0: 0, 1: 1, 2: 4, 3: 9}
set:             {0, 1, 2}
anidada:         [('a', 1), ('a', 2), ('b', 1), ('b', 2)]
generador:       <generator object <genexpr> at 0x722c09a4b5e0>  -> suma 285

Se leen de izquierda a derecha: «el cuadrado de n, para cada n de nums, si n es par». Sustituyen al bucle de tres líneas que crea una lista vacía y le va haciendo append, y son lo que más distingue el código de alguien que lleva tiempo con Python.

El paréntesis en vez del corchete hace un generador, que no calcula nada hasta que se le pide y no guarda la lista entera en memoria. Para sum(n*n for n in nums) no hace falta construir la lista: con un millón de elementos, la diferencia es real.
Dónde parar. Una comprensión con dos for y dos if ya es más difícil de leer que el bucle que sustituye. La regla práctica: si no cabe cómoda en una línea, o si el cuerpo hace algo más que transformar, escribe el bucle.

match

match x:
    case None:                      return "nada"
    case int() | float() if x > 10: return f"número grande: {x}"
    case int() | float():           return f"número: {x}"
    case str() as s:                return f"texto de {len(s)} letras"
    case [a, b]:                    return f"par ({a}, {b})"
    case [_, *resto]:               return f"lista con {len(resto)} más detrás"
    case {"tipo": t, **extra}:      return f"dict de tipo {t} con {len(extra)} campos más"
    case _:                         return "otra cosa"
salidanada
número grande: 42
número: 3.5
texto de 4 letras
par (1, 2)
lista con 2 más detrás
dict de tipo x con 2 campos más
otra cosa

Esto no es un switch: no compara valores, compara formas. Cada case describe una estructura —un entero, una lista de dos elementos, un diccionario con cierta clave— y de paso extrae las partes en variables. El if al final de un caso es una condición extra, y *resto recoge lo que sobre.

Es lo más útil que hay para procesar datos de forma desconocida: la respuesta de una API, un archivo de configuración, un mensaje. Llegó en Python 3.10.

Cuidado con un detalle que muerde: case Rojo: con un nombre a secas no compara con la variable Rojo, sino que captura el valor en una variable nueva llamada Rojo, y ese caso siempre coincide. Para comparar con una constante hay que cualificarla: case Color.ROJO:.

Verdadero y falso

salidabool(0) = False     bool(1) = True     bool('') = False     bool('x') = True
bool([]) = False    bool([0]) = True   bool({}) = False   bool(None) = False

En una condición, Python considera falso el cero, la cadena vacía, las colecciones vacías y None. Todo lo demás es verdadero. Por eso lo idiomático es:

if lista:          # en vez de  if len(lista) > 0:
if not texto:      # en vez de  if texto == "":
La excepción: para comparar con None se usa is None, no == None ni if not x. Porque 0, "" y [] también son falsos, y a veces son valores legítimos que no quieres confundir con «no hay nada».
Para… Se escribe
Recorrer con índice for i, x in enumerate(lista)
Recorrer dos a la vez for a, b in zip(xs, ys)
Transformar una lista una comprensión
Lo mismo sin gastar memoria la misma, con paréntesis
Distinguir por forma match
Preguntar si está vacío if not lista:
Comparar con None is None, siempre

Todo el código de esta lección se ejecutó con Python 3.14 en un contenedor limpio antes de publicarla; las salidas y los errores están copiados de esa ejecución.