Condicionales: tomar decisiones
"En el momento de la verdad solo hay dos clases de programadores: los que duermen tranquilos y los que prueban con datos." — folclore de la industria.
Qué vas a aprender en este capítulo
Hasta ahora tus programas hacen lo mismo siempre: la línea 1, después la 2, después la 3. En este capítulo aprendés a que el programa decida qué hacer según una condición — el primer ladrillo de la "inteligencia" que pone una computadora a trabajar de verdad. Vas a entender por qué Python usa indentación en vez de llaves, vas a evitar la trampa clásica = vs ==, y al final vas a hacer que la pupusería ofrezca distintos descuentos según la cantidad.
5.1 La idea: ramas en el camino
💡 Intuición
Imaginate manejando hacia UNIMO. Llegás a un semáforo. Si está en verde, seguís. Si está en rojo, frenás. Esa decisión — si pasa una cosa, hacé esto, si no, hacé aquello — es lo que en programación llamamos condicional.
Otro ejemplo: estás en la pupusería de la esquina. Si tu pedido es de 20 o más pupusas, te dan descuento. Si no, pagás precio lleno. Una condición, dos caminos.
Un condicional es la primera ruptura del orden lineal del programa. Hasta acá, las líneas se ejecutaban una tras otra. Con
if, algunas líneas se saltan según una condición.
En el mundo real las decisiones se ramifican: vas al campus → ¿hay clase? → si hay → ¿está mi profesor? → ... Cada if puede tener otros if adentro. Esa estructura ramificada es la columna vertebral de cualquier programa real.
📜 Historia
El primer lenguaje "de alto nivel" con condicionales fue FORTRAN (1957). Su IF era distinto del actual: comparaba un valor con cero y saltaba a una de tres etiquetas según fuera negativo, cero o positivo:
IF (X-Y) 100, 200, 300
Confuso, pero práctico para el hardware de la época. ALGOL 60 introdujo el if-then-else legible que conocemos hoy. La elegancia ganó: todos los lenguajes modernos heredan esa estructura.
Python le agregó una vuelta interesante: en lugar de { } o begin/end para marcar el bloque, usa la indentación. Eso fue diseñado a propósito para forzar a los programadores a sangrar el código de la misma forma que los humanos lo leemos. Causa peleas religiosas entre fans de los espacios y los que prefieren llaves; pero el resultado en Python es un código uniforme entre todos los que lo usan, al menos en lo de la indentación.
5.2 La forma básica: if
📐 Fundamento
if condicion:
# bloque que se ejecuta si la condición es verdadera
instruccion_1
instruccion_2
# acá ya estamos fuera del if
Anatomía:
- La palabra
if(siempre minúsculas). - Una condición — cualquier expresión que dé
TrueoFalse. - Dos puntos
:al final de la línea. - Un bloque indentado abajo.
Ejemplo:
edad = int(input("Edad: "))
if edad >= 18:
print("Sos mayor de edad.")
print("Podés votar.")
print("Gracias por usar el sistema.")
Si el usuario pone 25, imprime las tres líneas. Si pone 15, imprime solo la última — las indentadas se saltan.
La indentación es sintaxis, no decoración. En Python, los espacios al inicio de cada línea definen el bloque. La regla:
- Todo lo que está más indentado que el
ifpertenece al bloque delif. - La primera línea que vuelve al nivel del
if(o menos) está fuera.
Mezclar tabs y espacios da error (TabError). Convención del proyecto y del PEP 8: siempre 4 espacios, nunca tabs. Tu editor (VS Code, etc.) lo configura por vos.
⚠️ Trampa común
if x = 5. Mal: = asigna, == compara. Python te avisa con un SyntaxError. Lo correcto:
if x == 5:
...
En C y otros lenguajes esa confusión es más peligrosa porque no produce error de sintaxis (en C, if (x = 5) es legal y casi siempre incorrecto). Python te protege.
Olvidar los dos puntos.
if edad >= 18
print("Mayor")
Falta el : al final del if. Python protesta:
SyntaxError: expected ':'
Indentación inconsistente.
if edad >= 18:
print("Mayor")
print("Adulto") # un espacio menos: ¡error!
IndentationError: unindent does not match any outer indentation level
Acostumbrate a ver el indicador del editor (la barra vertical o las guías). Si las líneas no quedan alineadas, Python no las acepta.
5.3 else: el otro camino
if condicion:
# camino A
...
else:
# camino B
...
Exactamente uno de los dos bloques se ejecuta — nunca los dos, nunca ninguno.
edad = int(input("Edad: "))
if edad >= 18:
print("Mayor de edad.")
else:
print(f"Te faltan {18 - edad} años.")
5.4 elif: cadena de decisiones
Cuando hay más de dos opciones, se usa elif (abreviatura de "else if"):
nota = float(input("Nota (0-10): "))
if nota >= 9:
letra = "A"
elif nota >= 8:
letra = "B"
elif nota >= 7:
letra = "C"
elif nota >= 6:
letra = "D"
else:
letra = "F"
print(f"Tu nota: {letra}")
Cómo funciona la cadena. Python evalúa las condiciones de arriba hacia abajo. La primera que sea True ejecuta su bloque y se sale de toda la cadena. Si ninguna se cumple, ejecuta el else. Por eso el orden importa:
# MAL: la primera condición atrapa todo
if nota >= 6:
letra = "D"
elif nota >= 7: # ¡nunca se evalúa, porque >= 6 ya capturó!
letra = "C"
Regla: en una cadena if/elif, escribí las condiciones de la más restrictiva a la más laxa (o de mayor valor a menor, según el caso).
5.5 Condiciones compuestas
📐 Fundamento
Combiná comparaciones con and, or, not:
# AND: ambas condiciones tienen que cumplirse
if edad >= 18 and ciclo == 1:
print("Estudiante de primer ciclo, mayor de edad")
# OR: al menos una se cumple
if dia == "sabado" or dia == "domingo":
print("Fin de semana")
# NOT: invertir
if not está_lloviendo:
print("Salgo a correr")
Comparaciones encadenadas (lujo de Python):
if 0 <= nota <= 10:
print("Nota válida")
Es lo mismo que 0 <= nota and nota <= 10, pero más cercano a la matemática.
Operador in — pertenencia:
dia = input("Día: ").lower()
if dia in ("sabado", "domingo"):
print("Fin de semana")
in con una colección revisa si el valor está adentro. Lo vamos a usar con listas, diccionarios, cadenas, etc.
Combinaciones útiles con paréntesis:
# Aprueba si nota >= 7 Y asistencia >= 75%, O si proyecto es excelente
if (nota >= 7 and asistencia >= 0.75) or proyecto >= 9:
print("Aprobado")
Sin paréntesis, and tiene mayor precedencia que or. La expresión se interpreta como (nota>=7 and asistencia>=0.75) or proyecto>=9. Aún así, poné paréntesis — la claridad vale más que la economía de caracteres.
5.6 Anidación
Un if puede contener otros if:
nota = float(input("Nota: "))
asistencia = float(input("Asistencia (0-1): "))
if nota >= 6:
if asistencia >= 0.75:
print("Aprobado")
else:
print("Reprobado por inasistencia")
else:
print("Reprobado por nota")
Eso a veces se puede aplanar con condiciones compuestas:
if nota >= 6 and asistencia >= 0.75:
print("Aprobado")
elif nota >= 6:
print("Reprobado por inasistencia")
else:
print("Reprobado por nota")
Cuándo anidar y cuándo aplanar. Si los mensajes son distintos para cada caso, anidar es claro. Si hay un único mensaje "aprobado" y muchas razones de "no aprobado", aplanar con and es más limpio.
Demasiada anidación es un olor a código. Si estás en la cuarta sangría seguida de ifs, parate y pensá si podés:
- Combinar condiciones en una sola con
and/or. - Usar early return dentro de funciones (lo vemos en cap. 7).
- Reorganizar el flujo.
5.7 Operador ternario
Para asignar un valor según una condición, en una sola línea:
descuento = 0.05 if cantidad >= 20 else 0.0
Se lee: "descuento vale 0.05 si cantidad >= 20, sino 0.0".
Equivale a:
if cantidad >= 20:
descuento = 0.05
else:
descuento = 0.0
Cuándo usarlo. Asignaciones simples y claras. No abuses: si el ternario tiene 80 caracteres y tres operadores, escribí if/else normal. La economía no compensa la ilegibilidad.
5.8 Los datos como booleanos
Recordá del capítulo anterior: en Python, muchos valores se interpretan como True o False automáticamente.
| Valor | Como booleano |
|---|---|
0, 0.0, "", [], {}, (), None |
False |
| Cualquier otra cosa | True |
Eso te permite escribir:
nombre = input("Nombre: ")
if nombre:
print(f"Hola, {nombre}")
else:
print("Tenés que escribir tu nombre")
if nombre: es lo mismo que if nombre != "":. Es legible y muy "pythónico".
Ojo: if x == None se considera mal estilo. Usá if x is None (compara identidad, no valor). El motivo es sutil; por ahora, simplemente memorizalo.
5.9 match (Python 3.10+)
Cuando hay muchas comparaciones contra valores fijos, match queda más legible:
dia = input("Día: ").lower()
match dia:
case "lunes" | "martes" | "miercoles" | "jueves" | "viernes":
print("Día laboral")
case "sabado" | "domingo":
print("Fin de semana")
case _:
print("Día inválido")
case _: es el caso por defecto (equivalente al else).
match es mucho más poderoso que un switch clásico — soporta patrones estructurales (descomponer tuplas, listas, objetos). Para este libro nos basta este uso, pero conviene saber que existe. Lo retomamos cuando veamos tipos compuestos.
5.10 Proyecto: descuento por cantidad en la pupusería
🏗️ Avance del proyecto — Pupusería La Esquina
La pupusería ofrece una escala de descuentos:
- Menos de 10 pupusas: precio normal.
- 10 a 19: 5% de descuento.
- 20 a 49: 10%.
- 50 o más (mayoreo): 15%.
# Pupuseria La Esquina - Capitulo 5: descuentos por cantidad
precio_unitario = 0.50 # asumimos solo de queso para simplificar
IVA = 0.13
cantidad = int(input("Cuántas pupusas? "))
# Decidir el porcentaje de descuento
if cantidad >= 50:
porcentaje = 0.15
categoria = "Mayoreo"
elif cantidad >= 20:
porcentaje = 0.10
categoria = "Por docena"
elif cantidad >= 10:
porcentaje = 0.05
categoria = "Familiar"
else:
porcentaje = 0.0
categoria = "Normal"
# Cálculos
subtotal = cantidad * precio_unitario
descuento = subtotal * porcentaje
base = subtotal - descuento
impuesto = base * IVA
total = base + impuesto
# Recibo
print()
print("=== Recibo Pupuseria La Esquina ===")
print(f"Cantidad: {cantidad}")
print(f"Categoria: {categoria}")
print(f"Subtotal: ${subtotal:.2f}")
print(f"Descuento ({porcentaje:.0%}): -${descuento:.2f}")
print(f"Base imponible: ${base:.2f}")
print(f"IVA (13%): ${impuesto:.2f}")
print(f"TOTAL: ${total:.2f}")
Validación rudimentaria. Si el usuario pone un número negativo, las cuentas dan negativo y el "recibo" es absurdo. Idealmente queremos rechazar entradas inválidas:
if cantidad <= 0:
print("Cantidad inválida.")
else:
# ... el cálculo
Lo dejamos así por ahora. En el próximo capítulo, con bucles, vamos a poder pedir el dato hasta que sea válido.
5.11 Resumen visual
| Forma | Cuándo usarla |
|---|---|
if cond: |
Una sola decisión binaria. |
if/else |
Dos caminos mutuamente excluyentes. |
if/elif/.../else |
Tres o más caminos en cadena. |
match/case |
Comparar contra varios valores fijos (Python 3.10+). |
valor_si if cond else valor_no |
Asignación corta condicional. |
if x: |
Cuando "no vacío / no cero" es la condición. |
| Reglas oro |
|---|
| Indentá con 4 espacios, nunca tabs. |
Después del if/elif/else siempre va :. |
== para comparar, = para asignar. |
Poné paréntesis cuando combines and y or. |
5.12 Ejercicios
✏️ Ejercicio 5.1 — Par o impar
Pedile al usuario un entero y mostrale si es par o impar.
Solución
n = int(input("Número: "))
if n % 2 == 0:
print(f"{n} es par")
else:
print(f"{n} es impar")
% 2 == 0 es la prueba clásica de paridad. El módulo de un par entre 2 da 0, de un impar da 1.
✏️ Ejercicio 5.2 — Mayor de tres
Pedile tres notas y mostrá la mayor.
Solución
Versión con if anidados:
a = float(input("a: "))
b = float(input("b: "))
c = float(input("c: "))
if a >= b and a >= c:
mayor = a
elif b >= c:
mayor = b
else:
mayor = c
print(f"La mayor es {mayor}")
Versión más corta (con la función max):
print(max(a, b, c))
max recibe varios argumentos y devuelve el mayor. Está construida en Python — usala sin pena.
✏️ Ejercicio 5.3 — Año bisiesto
Un año es bisiesto si es divisible por 4, excepto si también es divisible por 100, a menos que también sea divisible por 400. Por ejemplo: 2000 es bisiesto, 1900 no, 2024 sí, 2023 no.
Pedile un año y decí si es bisiesto.
Solución
anio = int(input("Año: "))
if anio % 400 == 0:
bisiesto = True
elif anio % 100 == 0:
bisiesto = False
elif anio % 4 == 0:
bisiesto = True
else:
bisiesto = False
if bisiesto:
print(f"{anio} es bisiesto")
else:
print(f"{anio} NO es bisiesto")
Versión más corta (en una sola expresión):
bisiesto = (anio % 4 == 0 and anio % 100 != 0) or (anio % 400 == 0)
Las dos son válidas. La primera es más fácil de leer cuando uno está aprendiendo.
✏️ Ejercicio 5.4 — Triángulo válido
Tres números pueden ser los lados de un triángulo si cada lado es menor que la suma de los otros dos. Pedile al usuario tres lados y decí:
- Si forman un triángulo o no.
- Si lo forman: si es equilátero (3 iguales), isósceles (2 iguales) o escaleno (todos distintos).
Solución
a = float(input("Lado a: "))
b = float(input("Lado b: "))
c = float(input("Lado c: "))
if a + b > c and a + c > b and b + c > a:
if a == b == c:
print("Equilátero")
elif a == b or b == c or a == c:
print("Isósceles")
else:
print("Escaleno")
else:
print("No forman un triángulo")
Notá la cadena a == b == c — es comparación encadenada, una de las pocas formas de escribir "los tres son iguales" en una sola expresión.
✏️ Ejercicio 5.5 — Calculadora simple
Pedile dos números y una operación (+, -, *, /). Mostrá el resultado. Si la operación no es válida o se intenta dividir por cero, mostrá un mensaje claro.
Solución
a = float(input("a: "))
b = float(input("b: "))
op = input("Operación (+ - * /): ")
if op == "+":
print(a + b)
elif op == "-":
print(a - b)
elif op == "*":
print(a * b)
elif op == "/":
if b == 0:
print("Error: división por cero")
else:
print(a / b)
else:
print(f"Operación '{op}' no soportada")
Versión con match (Python 3.10+):
match op:
case "+": print(a + b)
case "-": print(a - b)
case "*": print(a * b)
case "/":
print("Error" if b == 0 else a / b)
case _: print(f"Operación '{op}' no soportada")
5.13 Para profundizar
- PEP 8 — indentación: https://peps.python.org/pep-0008/#indentation
- Documentación oficial sobre
match: https://docs.python.org/es/3/tutorial/controlflow.html#match-statements (PEP 634). - Próximo capítulo: Bucles — el otro pilar del control de flujo: repetir.
Definiciones nuevas: condicional, bloque, indentación, sentencia compuesta, condición, operador ternario, match/case.