Flujo de control
Todos los programas que has escrito hasta ahora se ejecutan de la misma forma: 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 te enseña cómo hacer que tu programa se ramifique y se repita.
Comparaciones
Antes de tomar una decisión, necesitas comparar cosas. Los operadores de comparación devuelven True o False. El más importante para dominar desde el principio: = asigna un valor, == comprueba si dos valores son iguales. Confundirlos es uno de los errores más comunes entre principiantes.
5 > 3 # True
5 < 3 # False
5 == 5 # True (nota: doble igual; = es asignación, == es comparación)
5 != 3 # True ("no es igual a")
5 >= 5 # True ("mayor o igual que")
5 <= 4 # False ("menor o igual que")La diferencia entre = y == confunde a casi todos al principio. La asignación (=) guarda un valor; la comparación (==) verifica si dos valores son iguales.
También puedes comparar strings. Python las compara alfabéticamente, carácter por carácter, 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)= guarda un valor, == comprueba si dos valores son iguales. Sigo bajando la velocidad y doble-comprobando ese cuando estoy cansado, así que estás en buena compañía si te confunde. Las comparaciones devuelven True o False, y funcionan en strings también, alfabéticamente. Combinar 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. Esto te permite expresar condiciones del mundo real como "la puntuación es satisfactoria AND 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 uno. not invierte.
and quiere ambos lados verdaderos, or quiere al menos uno, not invierte la respuesta. Eso cubre la mayoría de condiciones reales que escribirás, como "la puntuación es satisfactoria and el usuario está activo". Python deja de verificar en el momento en que sabe el resultado. Valores verdaderos y falsos
Cada valor en Python tiene una interpretación booleana, incluso si no es True o False. Las strings vacías, el cero, las 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: comprueba si una lista está no 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: comprueba si una string 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("Menor que 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 al final- Python usa indentación (4 espacios) para marcar qué pertenece dentro de cada bloque; no hay llaves
La indentación no es opcional ni 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 el primer coincidente se ejecuta. Lo que me confundió al principio: la indentación es lo que le dice a Python dónde empieza y termina cada bloque, así que mantenla consistente. Condiciones en una línea
Para asignaciones simples de sí/no, Python tiene una forma compacta en una línea llamada expresión ternaria: value_if_true if condition else value_if_false. Úsala solo cuando la lógica sea pequeña y lea 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 sea pequeña. Para cualquier cosa que implique 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 hermoso para pequeñas elecciones ordenadas como "aprobado" if score >= 50 else "reprobado". El momento en que la lógica crece más allá de eso, vuelve a un bloque normal if, se leerá mejor. Bucles while
Un bucle while repite su bloque mientras su condición es 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("Fin del juego")while es mejor cuando no sabes de antemano cuántas veces se ejecutará el bucle. Cuando lo sabes, o cuando estás iterando una colección, for es más limpio.
while sigue ejecutando su bloque mientras la condición se mantenga verdadera. Opta por él cuando no sabes 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 queden. continue salta el resto de la iteración actual y vuelve a la verificación de 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 # detiene 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 suceder al final del cuerpo del bucle.
continue salta el resto de la iteración actual y vuelve a la verificación de condición:
num = 0
while num < 10:
num += 1
if num % 2 == 0:
continue # salta números pares
print(num) # solo números impares se imprimen: 1, 3, 5, 7, 9break sale 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 hacer un bucle hasta que algo suceda. Bucles for
Un bucle for recorre una secuencia un elemento a la vez: una lista, una string, un rango de números. La variable que nombras después de for recibe cada elemento a su vez. No manejas un contador ni verificas la longitud tú mismo.
players = ["María", "Bob", "Carlos"]
for player in players:
print(f"¡Hola, {player}!")Los bucles for también funcionan en strings (iterando carácter por carácter) y en cualquier otro tipo de secuencia.
for item in sequence recorre cada elemento a su vez, y nunca tocas un contador o una longitud tú mismo. Funciona en listas, strings, rangos, muchas cosas. Viniendo de otros lenguajes, no manejar el índice se sintió extraño al principio, luego se sintió como un regalo. range()
range() genera una secuencia de números para que recorras. range(5) te da 0, 1, 2, 3, 4. Puedes controlar el inicio, el fin y el tamaño del paso. Úsalo cuando necesites que un bucle 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 | Lo que 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 que es eficiente incluso para rangos muy grandes.
range(5) te da 0, 1, 2, 3, 4, y puedes establecer un inicio, fin y paso. Opta por él cuando quieras que un bucle 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 recorres, así que no necesitas mantener un contador separado. La parte i, player automáticamente recibe un par de valores en cada iteración.
players = ["María", "Bob", "Carlos"]
for i, player in enumerate(players):
print(f"{i + 1}. {player}")
# 1. María
# 2. Bob
# 3. CarlosLa sintaxis i, player se llama desempaquetamiento. Python divide el par (index, value) en dos nombres automáticamente.
Por defecto enumerate() comienza en 0. Pasa un valor de inicio para cambiarlo:
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 sin contador separado para mantener sincronizado. La parte i, player atrapa 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 grillas, 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.
break y continue solo actúan en el bucle en el que están, el interior aquí. Loop-else
Los bucles de Python pueden tener una cláusula else que se ejecuta solo si el bucle termina sin golpear un break. No se usa comúnmente, pero es la forma más limpia de escribir "busca en una lista, y si no se encuentra nada, haz esto".
target = "Dave"
names = ["María", "Bob", "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.
else que se ejecute solo cuando el bucle termina sin un break. No se usa a menudo, pero es la forma más ordenada de decir "busca esta lista, y si nada coincidió, haz esto". El nombre es un poco engañoso, así que un comentario corto ayuda. 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 que no sea el valor crudo. Por ejemplo, ordenar nombres sin importar mayúsculas o ordenar tuplas de jugador 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", "Bob"]
sorted(names, key=str.lower) # ordenamiento sin importar mayúsculas
players = [("María", 87), ("Bob", 96), ("Carlos", 55)]
sorted(players, key=lambda p: p[1]) # ordena por puntuaciónlambda p: p[1] es una función en una línea: toma una tupla de jugador y devuelve la puntuación. Las lambdas reciben tratamiento completo en el capítulo Lambdas y comprensiones.
Para casos simples, usa sorted(). Para listas donde quieras modificar in-place, usa .sort().
sorted() entrega una nueva lista ordenada y deja el original solo, .sort() reacomoda la lista misma y devuelve None. Pasa key= para ordenar por algo que no sea el valor crudo, como nombres sin importar mayúsculas. Cuando dudes, opta por sorted(), es el seguro. En la práctica
Recorre puntuaciones, acumula un total, cuenta calificaciones aprobadas 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"Aprobadas: {passing}/{len(raw_scores)}")
print(f"Puntuación máxima: {sorted(raw_scores, reverse=True)[0]}")
