Flujo de control

Cada programa que has escrito hasta ahora funciona de la misma manera: de arriba hacia abajo, línea por línea. Eso funciona para scripts simples, pero los programas reales necesitan tomar decisiones y repetir trabajo. Un cuestionario necesita verificar si la respuesta es correcta. Un juego necesita seguir ejecutándose hasta que el jugador gane o pierda. Este capítulo cubre cómo hacer que tu programa se ramifique y repita.
Comparaciones
Antes de que puedas tomar una decisión, necesitas comparar cosas. Los operadores de comparación devuelven True o False. El más importante para acertar al principio: = asigna un valor, == verifica si dos valores son iguales. Confundirlos es uno de los errores más comunes de los principiantes.
5 > 3 # True
5 < 3 # False
5 == 5 # True (nota: doble igual; = es asignación, == es comparación)
5 != 3 # True ("no igual a")
5 >= 5 # True ("mayor o igual que")
5 <= 4 # False ("menor o igual que")La distinción = vs == confunde a casi todos al principio. La asignación (=) almacena un valor; la comparación (==) verifica si dos valores son iguales.
También puedes comparar cadenas. Python las compara alfabéticamente, carácter por carácter, lo que el capítulo Strings cubre con más detalle:
"apple" == "apple" # True
"apple" < "banana" # True (a viene antes que b)
"apple" == "Apple" # False (sensible a mayúsculas/minúsculas)= almacena un valor, == verifica si dos valores son iguales. Todavía voy lentamente y reviso eso cuando estoy cansado, así que estás en buena compañía si te confunde. Las comparaciones devuelven True o False, y también funcionan en cadenas, alfabéticamente. Combinando condiciones
and, or, y not combinan comparaciones. and requiere que ambos lados sean verdaderos. or requiere que al menos un lado sea verdadero. not invierte el resultado. Estos te permiten expresar condiciones del mundo real como "la puntuación es aprobatoria Y el usuario está activo".
age = 25
score = 88
age >= 18 and score >= 80 # True (ambos deben ser verdaderos)
age < 18 or score >= 80 # True (al menos uno debe ser verdadero)
not age >= 18 # False (invierte el resultado)and requiere ambos lados. or requiere al menos un lado. not invierte.
and quiere que ambos lados sean verdaderos, or quiere que al menos uno sea verdadero, not invierte la respuesta. Eso cubre la mayoría de condiciones reales que escribirás, como "la puntuación es aprobatoria and el usuario está activo". Python deja de verificar en el momento en que conoce el resultado. Truthy y falsy
Cada valor en Python tiene una interpretación booleana, incluso si no es True o False. Las cadenas vacías, cero, listas vacías, y None se comportan como False en una condición. Todo lo demás se comporta como True. Esto significa que if results: verifica si una lista no está vacía sin escribir if len(results) > 0:.
# Todos estos se comportan como False en una condición:
False, 0, 0.0, "", [], {}, (), None
# Todo lo demás se comporta como TrueEsto significa que if results: es una forma natural de decir "si la lista no está vacía", y if name: verifica si una cadena tiene contenido.
False en una condición: 0, "", [], y None. Todo lo demás cuenta como True. Por eso if results: se lee tan limpiamente como "si hay algo en la lista", sin len() necesario. if / elif / else
La declaración if ejecuta un bloque de código solo cuando su condición es True. elif añade más condiciones para verificar si la primera fue falsa. else captura todo lo que no coincidió con ninguna condición. Python usa indentación, no llaves, para definir qué pertenece dentro de cada bloque.
score = 87
if score >= 90:
print("Calificación A")
elif score >= 80:
print("Calificación B")
elif score >= 70:
print("Calificación C")
else:
print("Por debajo de C")Las reglas:
ifes obligatorio y siempre viene primeroelif(abreviatura de "else if") es opcional y puedes tener tantos como necesiteselsees opcional, maneja todo lo que no coincidió, y viene último- Python usa indentación (4 espacios) para marcar qué pertenece dentro de cada bloque; no hay llaves
La indentación no es opcional o cosmética. Python la usa para definir estructura. La indentación inconsistente es un error de sintaxis.
if ejecuta su bloque cuando la condición es verdadera, elif verifica más casos, else captura el resto. Solo la primera coincidencia se ejecuta. Lo que me atrapó al principio: la indentación es lo que le dice a Python dónde comienza y termina cada bloque, así que mantenla consistente. Condiciones de una línea
Para asignaciones simples sí/no, Python tiene una forma compacta de una línea llamada expresión ternaria: value_if_true if condition else value_if_false. Úsala solo cuando la lógica es pequeña y se lee como una oración.
label = "aprobado" if score >= 50 else "reprobado"Esta es una expresión ternaria; se lee como una oración. Úsala cuando la lógica es pequeña. Para cualquier cosa que involucre elif, escribe la versión completa.
value_if_true if condition else value_if_false elige uno de dos valores en una sola línea. Es encantador para pequeñas decisiones ordenadas como "aprobado" if score >= 50 else "reprobado". En el momento en que la lógica crece más allá de eso, vuelve a un bloque `if` normal, se leerá mejor. Bucles while
Un bucle while repite su bloque mientras su condición sea True. Úsalo cuando no sepas de antemano cuántas veces debe ejecutarse el bucle, por ejemplo esperando entrada válida o reintentando hasta que un trabajo tenga éxito.
lives = 3
while lives > 0:
print(f"Vidas restantes: {lives}")
lives -= 1
print("Juego terminado")while es mejor cuando no sabes de antemano cuántas veces se ejecutará el bucle. Cuando lo sabes, o cuando estás iterando sobre una colección, for es más limpio.
while mantiene ejecutando su bloque mientras la condición sea verdadera. Alcánzalo cuando no sepas cuántas rondas necesitas, como esperar entrada válida. Asegúrate de que algo dentro del bucle se mueva hacia la salida, o se ejecutará para siempre, un error que he cometido más de una vez. break y continue
break sale del bucle inmediatamente, sin importar cuántas iteraciones quedan. continue salta el resto de la iteración actual y vuelve a la verificación de la condición. Ambos solo afectan el bucle más interno en el que están.
break sale del bucle inmediatamente:
target = 5
num = 0
while True:
num += 1
if num == target:
print(f"Encontrado {target}")
break # detener el buclewhile True: con un break es un patrón válido y común cuando la condición de salida es compleja o necesita ocurrir al final del cuerpo del bucle.
continue salta el resto de la iteración actual y vuelve a la verificación de la condición:
num = 0
while num < 10:
num += 1
if num % 2 == 0:
continue # saltar números pares
print(num) # solo imprimen números impares: 1, 3, 5, 7, 9break salta fuera del bucle de inmediato, continue salta el resto de esta ronda y vuelve a verificar la condición. Ambos solo tocan el bucle en el que están directamente. while True: con un `break` se lee extraño al principio, pero es una forma normal de bucle hasta que algo sucede. Bucles for
Un bucle for recorre una secuencia un elemento a la vez: una lista, una cadena, un rango de números. La variable que nombras después de for recibe cada elemento a su turno. No necesitas manejar un contador o verificar la longitud tú mismo.
players = ["María", "Juan", "Carlos"]
for player in players:
print(f"¡Hola, {player}!")Los bucles for también funcionan en cadenas (iterando carácter por carácter) y en cualquier otro tipo de secuencia.
for item in sequence recorre cada elemento a su turno, y nunca tocas un contador o una longitud tú mismo. Funciona en listas, cadenas, rangos, muchas cosas. Viniendo de otros lenguajes, no manejar el índice se sintió extraño para mí al principio, luego se sintió como un regalo. range()
range() genera una secuencia de números para que la bucles. range(5) te da 0, 1, 2, 3, 4. Puedes controlar el inicio, fin y tamaño de paso. Úsalo cuando necesites un bucle que se ejecute un número específico de veces.
for i in range(5):
print(i) # 0, 1, 2, 3, 4range() tiene tres formas:
| Llamada | Qué produce |
|---|---|
range(5) | 0, 1, 2, 3, 4 |
range(2, 6) | 2, 3, 4, 5 |
range(0, 10, 2) | 0, 2, 4, 6, 8 (paso de 2) |
range(5, 0, -1) | 5, 4, 3, 2, 1 (contando hacia atrás) |
range() no crea una lista. Produce números uno a la vez, lo cual es eficiente incluso para rangos muy grandes.
range(5) te da 0, 1, 2, 3, 4, y puedes establecer un inicio, fin y paso. Alcánzalo cuando quieras un bucle que se ejecute un número establecido de veces. No construye una lista, te entrega un número por ronda. enumerate()
enumerate() te da tanto el índice como el valor mientras buclas, así que no necesitas rastrear un contador por separado. La parte i, player automáticamente recibe un par de valores en cada iteración.
players = ["María", "Juan", "Carlos"]
for i, player in enumerate(players):
print(f"{i + 1}. {player}")
# 1. María
# 2. Juan
# 3. CarlosLa sintaxis i, player se llama desempaque. Python divide el par (index, value) en dos nombres automáticamente.
Por defecto enumerate() comienza en 0. Pasa un valor de inicio para cambiar eso:
for i, player in enumerate(players, start=1):
print(f"{i}. {player}") # comienza en 1enumerate() te entrega la posición y el valor juntos, así que no hay contador separado que mantener en sincronía. La parte `i, player` captura ambos a la vez. Pasa `start=1` cuando estés numerando una lista para que alguien lea. Bucles anidados
Puedes poner un bucle dentro de otro bucle. El bucle interior se ejecuta completamente para cada una de las iteraciones del bucle exterior. Así es como procesas cuadrículas, combinaciones, o cualquier dato con dos niveles de estructura.
rows = [1, 2, 3]
cols = ["A", "B"]
for row in rows:
for col in cols:
print(f"{col}{row}", end=" ")
print() # nueva línea después de cada fila
# A1 B1
# A2 B2
# A3 B3break y continue dentro de un bucle anidado solo afectan el bucle más interno.
else de bucle
Los bucles de Python pueden tener una cláusula else que se ejecuta solo si el bucle terminó sin golpear un break. No es comúnmente usado, pero es la forma más limpia de escribir "busca una lista, y si nada fue encontrado, haz esto".
target = "David"
names = ["María", "Juan", "Carlos"]
for name in names:
if name == target:
print(f"Encontrado {target}")
break
else:
print(f"{target} no está en la lista") # se ejecuta porque break nunca se disparóSi break se ejecuta, el else se salta. Si el bucle agota la secuencia, else se ejecuta. Es un patrón de nicho pero más limpio que una variable bandera.
Ordenamiento
sorted() devuelve una nueva lista ordenada y deja el original sin cambios. .sort() ordena la lista en su lugar y devuelve None. El argumento key= te permite ordenar por algo distinto del valor bruto. Por ejemplo, ordenar nombres sin distinguir entre mayúsculas/minúsculas u ordenar tuplas de jugadores por su puntuación.
scores = [87, 42, 96, 55, 71]
ranked = sorted(scores) # [42, 55, 71, 87, 96] (nueva lista)
scores.sort() # ordena la lista original, devuelve None
scores.sort(reverse=True) # [96, 87, 71, 55, 42]Ambos aceptan un argumento key=: una función aplicada a cada elemento antes de la comparación:
names = ["Carlos", "María", "Juan"]
sorted(names, key=str.lower) # ordenamiento sin distinguir mayúsculas
players = [("María", 87), ("Juan", 96), ("Carlos", 55)]
sorted(players, key=lambda p: p[1]) # ordena por puntuaciónlambda p: p[1] es una función de una línea: toma una tupla de jugador y devuelve la puntuación. Las lambdas obtienen tratamiento completo en el capítulo Lambdas y comprensiones.
Para casos simples, usa sorted(). Para listas donde quieres modificar en su lugar, usa .sort().
sorted() devuelve una nueva lista ordenada y deja el original solo, .sort() reorganiza la lista misma y devuelve None. Pasa `key=` para ordenar por algo distinto del valor bruto, como nombres sin distinguir mayúsculas. Cuando esté en duda, alcanza `sorted()`, es el seguro. En práctica
Recorre puntuaciones, acumula un total, cuenta calificaciones aprobatorias, e imprime un resumen:
raw_scores = [87, 42, 96, 55, 71, 63]
total = 0
passing = 0
for score in raw_scores:
total += score
if score >= 60:
passing += 1
average = total / len(raw_scores)
print(f"Promedio: {average:.1f}")
print(f"Aprobatoria: {passing}/{len(raw_scores)}")
print(f"Puntuación superior: {sorted(raw_scores, reverse=True)[0]}")
