Condicionales e Iteración
Domina el flujo de ejecución: bifurcaciones, bucles y estructuras de control en Python
"¿Me podría decir, por favor, qué camino debo tomar desde aquí?" "Eso depende en gran medida de adónde quieras llegar." — Lewis Carroll, Alicia en el País de las Maravillas
En ciencias de la computación, el flujo de control es el orden en que las instrucciones individuales de un programa son ejecutadas o evaluadas. Las dos formas principales de controlar el flujo son la programación condicional (branching) y los bucles (looping). Juntas, permiten crear una variedad infinita de programas.
Programación Condicional
La programación condicional es algo que hacemos cada momento del día. Consiste en evaluar condiciones y decidir qué acción tomar: si la luz está verde, puedo cruzar; si está lloviendo, llevaré paraguas; si llego tarde al trabajo, llamaré a mi jefe.
La Sentencia if
La herramienta principal para la programación condicional en Python es la sentencia if. Su función es evaluar una expresión y, basándose en el resultado, elegir qué parte del código ejecutar:
# Ejemplo básico de if late = True if late: print("¡Necesito llamar a mi jefe!") # Con cláusula else late = False if late: print("¡Necesito llamar a mi jefe!") else: print("No necesito llamar a mi jefe...")
if evalúa la expresión en un contexto booleano (como si llamara a bool(late)). Si el resultado es True, se ejecuta el bloque indentado después del if.
La Cláusula elif
A veces necesitas más de dos caminos para elegir. Veamos un calculador de impuestos donde el porcentaje depende de tu ingreso:
income = 15000 if income < 10000: tax_coefficient = 0.0 # Sin impuestos elif income < 30000: tax_coefficient = 0.2 # 20% elif income < 100000: tax_coefficient = 0.35 # 35% else: tax_coefficient = 0.45 # 45% print(f"Pagarás: ${income * tax_coefficient} en impuestos")
| Ingreso | Coeficiente | Impuesto |
|---|---|---|
| < $10,000 | 0% | $0 |
| $10,000 - $29,999 | 20% | Variable |
| $30,000 - $99,999 | 35% | Variable |
| ≥ $100,000 | 45% | Variable |
El Operador Ternario
Cuando solo necesitas elegir entre dos valores según una condición, el operador ternario ofrece una sintaxis más concisa:
if order_total > 100: discount = 25 else: discount = 0
discount = 25 if order_total > 100 else 0
Pattern Matching (match/case)
Introducido en Python 3.10, el structural pattern matching permite comparar un valor contra múltiples patrones de forma elegante:
day_number = 4 match day_number: case 1 | 2 | 3 | 4 | 5: print("Día laboral") case 6: print("Sábado") case 7: print("Domingo") case _: print(f"{day_number} no es un número válido")
_ es un comodín (wildcard) que coincide con cualquier valor. Funciona como el else final y debe ser el último case.
Bucles (Looping)
Los bucles permiten repetir la ejecución de un bloque de código más de una vez. Python ha destilado todos los constructos de bucle en solo dos: for y while.
El Bucle for
El bucle for se usa para iterar sobre una secuencia como una lista, tupla o colección de objetos:
# Iterando sobre una lista for number in [0, 1, 2, 3, 4]: print(number) # Usando range() - más eficiente for number in range(5): print(number)
La Función range()
La función range() genera secuencias de números. Acepta uno, dos o tres argumentos:
# Un valor: de 0 hasta el valor (excluido) list(range(10)) # → [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # Dos valores: start hasta stop (excluido) list(range(3, 8)) # → [3, 4, 5, 6, 7] # Tres valores: start, stop y step list(range(-10, 10, 4)) # → [-10, -6, -2, 2, 6]
Iterando con enumerate()
Cuando necesitas tanto el índice como el valor, usa enumerate() en lugar de range(len(...)):
surnames = ["Rivest", "Shamir", "Adleman"] for i in range(len(surnames)): print(i, surnames[i])
surnames = ["Rivest", "Shamir", "Adleman"] for i, surname in enumerate(surnames): print(i, surname)
Iterando Múltiples Secuencias con zip()
La función zip() permite iterar sobre múltiples secuencias en paralelo:
people = ["Nick", "Rick", "Roger", "Syd"] ages = [23, 24, 23, 21] instruments = ["Drums", "Keyboards", "Bass", "Guitar"] for person, age, instrument in zip(people, ages, instruments): print(person, age, instrument)
Rick 24 Keyboards
Roger 23 Bass
Syd 21 Guitar
El Bucle while
El bucle while se ejecuta mientras una condición sea verdadera. Es ideal cuando no sabes cuántas iteraciones necesitarás:
# Convertir decimal a binario n = 39 remainders = [] while n > 0: n, remainder = divmod(n, 2) remainders.append(remainder) remainders.reverse() print(remainders) # → [1, 0, 0, 1, 1, 1] = 39 en binario
False, el bucle se convierte en un bucle infinito. Estos se usan intencionalmente para cosas como servidores que escuchan peticiones continuamente.
Control de Flujo en Bucles
A veces necesitas alterar el flujo normal de un bucle. Python proporciona dos sentencias para esto: break para salir completamente del bucle, y continue para saltar a la siguiente iteración.
Usando continue
Supongamos que quieres aplicar un 20% de descuento solo a productos que expiran hoy:
from datetime import date, timedelta today = date.today() tomorrow = today + timedelta(days=1) products = [ {"sku": "1", "expiration_date": today, "price": 100.0}, {"sku": "2", "expiration_date": tomorrow, "price": 50}, {"sku": "3", "expiration_date": today, "price": 20}, ] for product in products: print("Procesando sku", product["sku"]) if product["expiration_date"] != today: continue # Salta al siguiente producto product["price"] *= 0.8 print("Sku", product["sku"], "precio ahora", product["price"])
Sku 1 precio ahora 80.0
Procesando sku 2
Procesando sku 3
Sku 3 precio ahora 16.0
Usando break
Si necesitas encontrar el primer elemento que cumple una condición y luego detenerte:
items = [0, None, 0.0, True, 0, 7] found = False for item in items: print("escaneando item", item) if item: found = True break # Sale del bucle completamente if found: print("Al menos un item evalúa a True") else: print("Todos los items evalúan a False")
escaneando item None
escaneando item 0.0
escaneando item True
Al menos un item evalúa a True
La Cláusula else en Bucles
Python tiene una característica única: puedes agregar un else después de un bucle. El bloque else se ejecuta solo si el bucle no fue interrumpido por break:
class DriverException(Exception): pass people = [("James", 17), ("Kirk", 9), ("Lars", 13), ("Robert", 8)] for person, age in people: if age >= 18: driver = (person, age) break else: # Se ejecuta solo si NO hubo break raise DriverException("Conductor no encontrado.")
else de un bucle como nobreak: "Si el bucle terminó sin break, entonces ejecuta este bloque".
Expresiones de Asignación
Python 3.8 introdujo el operador walrus (:=) que permite asignar valores a variables dentro de expresiones, donde normalmente no se permitiría una asignación.
Simplificando Condiciones
remainder = value % modulus if remainder: print(f"Resto: {remainder}")
if (remainder := value % modulus): print(f"Resto: {remainder}")
Simplificando Bucles while
flavors = ["pistacho", "malaga", "vainilla", "chocolate"] prompt = "Elige tu sabor: " print(flavors) # El walrus asigna Y evalúa en la misma línea while (choice := input(prompt)) not in flavors: print(f"Lo siento, '{choice}' no es una opción válida.") print(f"Elegiste '{choice}'.")
Ejemplo Práctico: Generador de Primos
Combinemos todo lo aprendido para crear un generador de números primos. Un número primo es mayor que 1 y solo es divisible por 1 y por sí mismo.
primes = [] upto = 100 for n in range(2, upto + 1): for divisor in range(2, n): if n % divisor == 0: break else: # nobreak: n es primo primes.append(n) print(primes)
else del bucle interno se ejecuta solo si ningún divisor dividió exactamente a n (es decir, no hubo break). Esto indica que n es primo.
Lookup Tables: Aplicando Descuentos
En lugar de usar múltiples if/elif, una técnica elegante es usar diccionarios como tablas de búsqueda:
customers = [ dict(id=1, total=200, coupon_code="F20"), dict(id=2, total=150, coupon_code="P30"), dict(id=3, total=100, coupon_code="P50"), ] # Lookup table: (porcentaje, fijo) discounts = { "F20": (0.0, 20.0), # Fijo £20 "P30": (0.3, 0.0), # 30% "P50": (0.5, 0.0), # 50% "F15": (0.0, 15.0), # Fijo £15 } for customer in customers: code = customer["coupon_code"] percent, fixed = discounts.get(code, (0.0, 0.0)) customer["discount"] = percent * customer["total"] + fixed for c in customers: print(c["id"], c["total"], c["discount"])
2 150 45.0
3 100 50.0
El Módulo itertools
El módulo itertools implementa bloques de construcción de iteradores inspirados en APL, Haskell y SML. Proporciona tres categorías de iteradores.
Iteradores Infinitos
from itertools import count for n in count(5, 3): # Empieza en 5, suma 3 cada vez if n > 20: break print(n, end=", ")
Iteradores de Terminación Corta
from itertools import compress data = range(10) even_selector = [1, 0] * 10 odd_selector = [0, 1] * 10 even_numbers = list(compress(data, even_selector)) odd_numbers = list(compress(data, odd_selector)) print("Pares:", even_numbers) print("Impares:", odd_numbers)
Impares: [1, 3, 5, 7, 9]
Generadores Combinatorios
from itertools import permutations print(list(permutations("ABC"))) # N elementos tienen N! permutaciones # "ABC" tiene 3! = 6 permutaciones
Resumen del Capítulo
01 // Condicionales
if/elif/else para bifurcar la ejecución. Operador ternario para asignaciones condicionales simples. match/case para múltiples patrones.
02 // Bucle for
Itera sobre secuencias e iterables. Usa enumerate() para índices y zip() para múltiples secuencias en paralelo.
03 // Bucle while
Ejecuta mientras una condición sea verdadera. Ideal cuando no conoces el número de iteraciones de antemano.
04 // Control de Flujo
break sale del bucle, continue salta a la siguiente iteración. else en bucles se ejecuta si no hubo break.
05 // Walrus Operator
:= permite asignar y usar un valor en la misma expresión. Útil en condiciones de while e if.
06 // itertools
Módulo poderoso con iteradores infinitos, de terminación corta y combinatorios. Explóralo para soluciones elegantes.