Clase 4 — Condicionales y flujo de programa
Por defecto, Python ejecuta las instrucciones de un programa línea a línea, de arriba a abajo. Cada línea se ejecuta exactamente una vez y en orden. A esto se le llama flujo secuencial.
Pero casi ningún programa real es puramente secuencial. Para que un programa pueda tomar decisiones, repetir acciones o reutilizar código, necesita alterar ese flujo. En Python hay tres mecanismos para hacerlo:
Decisión
Ejecutar un bloque u otro según una condición
if / elif / else
match / case
← esta clase
Repetición
Ejecutar un bloque múltiples veces
for
while
→ Clase 5
Llamada a función
Saltar a otro bloque de código y volver
def
return
→ Clase 6
— Flujo secuencial vs flujo con decisión —
# Flujo secuencial: todas las líneas se ejecutan siempre
temperatura = 35
print("Comprobando temperatura...")
print(f"Temperatura actual: {temperatura}°C")
print("Temperatura comprobada.")
# ¿Cómo avisamos solo si hace mucho calor?
# Con flujo secuencial no podemos — se ejecuta todo siempre
# Flujo con decisión: solo se ejecuta el bloque correcto
temperatura = 35
print("Comprobando temperatura...")
if temperatura > 30:
print("⚠ Alerta: temperatura muy alta")
print("Recomendación: mantente hidratado")
print("Temperatura comprobada.")
# Cambia temperatura a 20 y observa qué cambia
Las dos líneas dentro del if
solo se ejecutan si la condición es verdadera.
La última línea se ejecuta siempre —
está fuera del bloque condicional.
La indentación es la que
define qué pertenece al bloque y qué no.
No es una cuestión de estilo: en Python
la indentación es sintaxis.
— El error más peligroso: flujo incorrecto —
En la Clase 1 distinguimos dos tipos de error: el error de Nivel 1 produce un mensaje de error visible — el programa se detiene y avisa. El error de Nivel 2 es silencioso — el programa termina sin error pero el resultado es incorrecto.
El flujo incorrecto es la fuente más frecuente de errores de Nivel 2. Una condición mal escrita no produce ningún mensaje — simplemente ejecuta el bloque equivocado, o no ejecuta ninguno cuando debería, o ejecuta uno cuando no debería.
# Error de Nivel 2: condición incorrecta, sin mensaje de error
nota = 4.9
# ❌ Condición mal escrita — ¿qué imprime?
if nota > 5:
print("Aprobado")
else:
print("Suspenso")
# ✓ Condición correcta — ¿cambia el resultado?
if nota >= 5:
print("Aprobado")
else:
print("Suspenso")
# El programa no produce ningún error en ninguno de los dos casos
# Solo el resultado final revela cuál es correcto
> vs >=,
and vs or,
el orden de los elif.
Ninguno de estos errores produce un mensaje.
Solo los detectas leyendo el código
o probando casos límite.
temperatura = 35
a temperatura = 20
y predice qué se imprime antes de ejecutar.
¿Cuántas líneas se ejecutan en cada caso?
nota = 4.9?
¿Y con nota = 5.0?
¿Cuál de las dos condiciones
(> vs >=)
es la correcta para un sistema
donde el 5.0 es aprobado?
SyntaxError?
¿En qué tipo de situaciones
podría tener consecuencias graves
en un análisis de datos real?
Con temperatura = 20:
se imprime "Comprobando temperatura..."
y "Temperatura comprobada."
— las dos líneas fuera del bloque.
Las dos líneas dentro del if
no se ejecutan porque la condición
20 > 30 es False.
Con temperatura = 35
se ejecutan las 4 líneas.
Con nota = 4.9:
ambas versiones imprimen "Suspenso"
— en este caso el resultado es el mismo.
Con nota = 5.0:
la versión con >
imprime "Suspenso"
porque 5.0 > 5
es False.
La versión con >=
imprime "Aprobado"
porque 5.0 >= 5
es True.
La correcta para un sistema donde
el 5.0 aprueba es >=.
Un SyntaxError detiene
el programa inmediatamente y señala
exactamente dónde está el problema.
Es imposible ignorarlo.
Un error de flujo incorrecto deja
que el programa termine con normalidad —
produce un resultado que parece válido
pero no lo es.
En análisis de datos las consecuencias pueden ser graves: clasificar clientes incorrectamente, aplicar un descuento a quien no le corresponde, excluir registros válidos de un análisis o incluir registros que deberían filtrarse. Ninguno de estos errores produce un mensaje — solo los detectas conociendo el resultado esperado o probando casos límite.
if
básico: condición e indentación
teoría
Un if evalúa una expresión booleana
— una pregunta cuya respuesta es
True o False.
Si la respuesta es True,
ejecuta el bloque de código que tiene debajo.
Si es False, lo salta.
La sintaxis tiene dos partes obligatorias:
los dos puntos al final
de la línea del if,
y la indentación del bloque
que se ejecuta si la condición es verdadera.
En Python la indentación no es estilo —
es sintaxis. Define qué instrucciones
pertenecen al bloque.
— Sintaxis básica —
# Estructura mínima
if condición:
instrucción_1 # se ejecuta si condición es True
instrucción_2 # también dentro del bloque
instrucción_3 # fuera del bloque — se ejecuta siempre
# Ejemplo concreto
x = 10
y = 20
if x < y:
print("x es menor que y")
print("Esto se imprime siempre")
— La indentación define el bloque —
La indentación estándar en Python es
4 espacios.
JupyterLab la añade automáticamente
después de los dos puntos.
Si la indentación es incorrecta,
Python lanza un IndentationError
— uno de los pocos errores de flujo
que sí produce un mensaje visible.
# Correcto: instrucción dentro del bloque
x = 15
if x > 10:
print("x es mayor que 10") # 4 espacios
print("esto también está dentro")
# La indentación define qué está dentro y qué fuera
temperatura = 38
if temperatura > 37.5:
print("Fiebre detectada") # dentro — solo si fiebre
print("Consulta con un médico") # dentro — solo si fiebre
print("Medición completada") # fuera — siempre
# Error de indentación — produce IndentationError
if x > 10:
print("falta indentación") # ← error
— La condición es cualquier expresión que devuelva True o False —
x = 10
nombre = "Ana"
numeros = [1, 2, 3]
# Comparación numérica
if x > 5:
print("x es mayor que 5")
# Comparación de strings
if nombre == "Ana":
print(f"Hola, {nombre}")
# Pertenencia
if 2 in numeros:
print("El 2 está en la lista")
# Variable booleana directamente
es_valido = True
if es_valido:
print("Registro válido")
# Resultado de una función
if len(numeros) > 0:
print(f"La lista tiene {len(numeros)} elementos")
a = 7
b = 7
if a == b:
print("iguales")
print("ambos valen 7")
print("fin")
temperatura = 36.8?
¿Y si temperatura = 38.2?
Indica para cada caso qué líneas
se ejecutan y cuáles no.
precio = 150
if precio > 100:
print("precio alto")
print("aplicar descuento")
edad:
17, 18,
65:
edad = 18
if edad >= 18:
print("acceso permitido")
print(f"edad registrada: {edad}")
Imprime las tres líneas:
"iguales",
"ambos valen 7"
y "fin".
La condición a == b
es True porque
ambas variables valen 7,
así que el bloque completo se ejecuta.
"fin" se imprime siempre
porque está fuera del bloque.
Con temperatura = 36.8:
la condición 36.8 > 37.5
es False — solo se ejecuta
"Medición completada".
Las dos líneas del bloque se saltan.
Con temperatura = 38.2:
la condición es True
— se ejecutan las tres líneas:
"Fiebre detectada",
"Consulta con un médico"
y "Medición completada".
Es un IndentationError
en la línea del primer print
— le falta la indentación de 4 espacios.
La segunda línea tiene indentación
pero la primera no, lo que produce
un error de sintaxis.
La corrección:
precio = 150
if precio > 100:
print("precio alto")
print("aplicar descuento")
Con edad = 17:
17 >= 18 es False
— solo imprime
"edad registrada: 17".
Con edad = 18:
18 >= 18 es True
— imprime "acceso permitido"
y "edad registrada: 18".
Con edad = 65:
igual que el caso 18 — imprime ambas líneas.
edad = 18 — exactamente
en la frontera. Con >=
el acceso está permitido.
Con > no lo estaría.
Siempre prueba los valores
exactamente en el límite
de la condición.
elif
y
else:
eficiencia en las decisiones
teoría
Cuando hay más de dos posibilidades,
la primera tentación es encadenar
varios if independientes.
Funciona, pero tiene un problema:
Python evalúa todas
las condiciones aunque ya haya encontrado
una verdadera. Es ineficiente y puede
producir resultados inesperados si los
bloques se interfieren entre sí.
elif y else
resuelven esto: forman una cadena
donde Python evalúa las condiciones
en orden y se detiene
en la primera que sea verdadera.
El resto no se evalúa.
— Múltiples if vs elif: la diferencia —
x = 10
y = 20
# ❌ Múltiples if: evalúa las TRES condiciones siempre
if x < y:
print("x es menor que y")
if x > y:
print("x es mayor que y")
if x == y:
print("x es igual a y")
# ✓ elif: evalúa solo las necesarias — se detiene en la primera True
if x < y:
print("x es menor que y")
elif x > y:
print("x es mayor que y")
elif x == y:
print("x es igual a y")
Con x = 10 y y = 20,
la primera condición es True.
Con múltiples if, Python evalúa
las tres condiciones de todas formas —
dos evaluaciones innecesarias.
Con elif, encuentra la primera
True y no evalúa las siguientes.
Para tres condiciones la diferencia es pequeña.
Para cien condiciones, es enorme.
— else: el caso por defecto —
else captura todos los casos
que no han satisfecho ninguna condición anterior.
No tiene condición propia — se ejecuta
si y solo si ningún if
ni elif anterior fue True.
La ventaja del else es que
elimina la última condición explícita —
si algo no es menor ni mayor, debe ser igual.
No hace falta escribirlo.
Además garantiza que siempre se ejecuta
al menos un bloque.
x = 10
y = 10
# Con elif final — todavía necesita evaluar la última condición
if x < y:
print("x es menor que y")
elif x > y:
print("x es mayor que y")
elif x == y: # esta condición es redundante
print("x es igual a y")
# Con else — más limpio y eficiente
if x < y:
print("x es menor que y")
elif x > y:
print("x es mayor que y")
else:
print("x es igual a y") # se ejecuta si no es menor ni mayor
— Cómo fluye la ejecución —
Múltiples if — evalúa todo
inicio ↓ ¿x < y? → sí → bloque A ↓ (siempre) ¿x > y? → sí → bloque B ↓ (siempre) ¿x == y? → sí → bloque C ↓ fin
if / elif / else — se detiene
inicio ↓ ¿x < y? → sí → bloque A → fin ↓ no ¿x > y? → sí → bloque B → fin ↓ no else → bloque C → fin
— Cuándo usar if independientes vs elif —
No siempre es mejor elif.
La elección depende de si las condiciones
son mutuamente excluyentes
o no.
# Condiciones NO excluyentes → múltiples if independientes
# Un dato puede cumplir VARIAS condiciones a la vez
valor = 85
if valor > 50:
print("supera la mitad")
if valor > 80:
print("supera el umbral alto")
if valor > 90:
print("supera el umbral máximo")
# Las dos primeras son True — queremos que ambas se ejecuten
# Condiciones SÍ excluyentes → if / elif / else
# Un dato solo puede caer en UNA categoría
nota = 85
if nota >= 90:
print("Sobresaliente")
elif nota >= 70:
print("Notable")
elif nota >= 50:
print("Aprobado")
else:
print("Suspenso")
# Una nota no puede ser Notable y Aprobado a la vez
x = 10 y y = 20:
¿cuántas condiciones evalúa la versión
con múltiples if?
¿Y la versión con elif?
¿Qué imprime cada una?
nota = 85:
nota = 85
if nota >= 90:
print("Sobresaliente")
elif nota >= 70:
print("Notable")
elif nota >= 50:
print("Aprobado")
else:
print("Suspenso")
¿Qué ocurre si cambias
el orden de los elif
y pones el >= 50 primero?
valor = 85:
¿cuántas líneas se imprimen?
¿Qué pasaría si usaras
elif en lugar de
múltiples if?
temperatura = 38
if temperatura > 36:
print("temperatura normal")
elif temperatura > 37.5:
print("fiebre leve")
elif temperatura > 39:
print("fiebre alta")
else:
print("hipotermia")
Con múltiples if:
evalúa las tres condiciones.
Con elif: evalúa solo
la primera — 10 < 20
es True y se detiene.
Ambas versiones imprimen
"x es menor que y"
— el resultado es el mismo pero
el camino es distinto.
Con nota = 85 imprime
"Notable" — la primera
condición verdadera es
85 >= 70.
El resto no se evalúa.
Si pones >= 50 primero,
con nota = 85 imprimiría
"Aprobado" — incorrecto.
Porque 85 >= 50 es
True y el elif
se detiene ahí sin llegar al
>= 70.
elif
las condiciones más restrictivas
deben ir primero. Si pones
la condición más amplia al inicio,
los casos más específicos
nunca se alcanzan.
Con valor = 85
se imprimen dos líneas:
"supera la mitad"
y "supera el umbral alto"
— las dos primeras condiciones
son True y como son
if independientes,
ambas se ejecutan.
Con elif solo
se imprimiría la primera
"supera la mitad"
y las demás se saltarían —
resultado incorrecto para este caso.
El error está en el orden de condiciones.
Con temperatura = 38
la primera condición
38 > 36 es True
— imprime "temperatura normal"
cuando debería imprimir
"fiebre leve".
Las condiciones de fiebre nunca
se alcanzan porque la primera
es demasiado amplia.
La corrección: ordenar de más
restrictivo a menos restrictivo.
if temperatura > 39:
print("fiebre alta")
elif temperatura > 37.5:
print("fiebre leve")
elif temperatura > 36:
print("temperatura normal")
else:
print("hipotermia")
elif
con rangos numéricos,
siempre empieza por el extremo
más restrictivo — el valor
más alto si usas >,
el más bajo si usas <.
Un condicional puede contener otro condicional dentro de su bloque. A esto se le llama anidamiento. Es útil cuando la segunda decisión solo tiene sentido si la primera ya fue verdadera.
Sin embargo, el anidamiento excesivo
produce código difícil de leer —
cada nivel añade 4 espacios más de indentación
y el flujo se vuelve difícil de seguir.
Casi siempre hay una forma más clara
de escribir lo mismo usando elif
o combinando condiciones con and.
— Condicionales anidados —
# Anidamiento: la segunda decisión depende de la primera
edad = 20
tiene_carnet = True
if edad >= 18:
print("Es mayor de edad")
if tiene_carnet:
print("Puede conducir")
else:
print("No tiene carnet")
else:
print("Es menor de edad — no puede conducir")
# La misma lógica con and — más compacto
if edad >= 18 and tiene_carnet:
print("Puede conducir")
elif edad >= 18:
print("Mayor de edad pero sin carnet")
else:
print("Menor de edad")
Las dos versiones producen el mismo resultado
pero la segunda es más fácil de leer
de un vistazo. En general, si puedes
evitar el anidamiento combinando condiciones
con and u or,
el código gana en claridad.
El Zen de Python lo dice directamente:
"Flat is better than nested."
— El ejemplo de notas: de la versión larga a la simplificada —
Un patrón muy frecuente en código generado
por IA es usar and para definir
rangos numéricos explícitamente —
score >= 90 and score <= 100.
Funciona, pero es más largo de lo necesario.
Aprovechando que elif solo
se evalúa si las anteriores fueron falsas,
podemos simplificar enormemente.
# Versión larga — con and explícito en cada rango
score = 85
if score >= 90 and score <= 100:
print("Sobresaliente")
elif score >= 70 and score < 90:
print("Notable")
elif score >= 50 and score < 70:
print("Aprobado")
else:
print("Suspenso")
# Versión simplificada — aprovechando el cortocircuito del elif
# Si llegamos al elif >= 70, YA SABEMOS que score < 90
# porque si fuera >= 90 habría entrado en el primer if
score = 85
if score >= 90:
print("Sobresaliente")
elif score >= 70:
print("Notable")
elif score >= 50:
print("Aprobado")
else:
print("Suspenso")
elif score >= 70,
ya sabe que score < 90 —
de lo contrario habría entrado en el
if score >= 90 anterior
y nunca habría llegado hasta aquí.
El elif hereda implícitamente
la negación de todas las condiciones anteriores.
— Comparaciones encadenadas: pythónico y conciso —
Python permite encadenar comparaciones
de forma natural — como en matemáticas.
90 <= score <= 100
es equivalente a
score >= 90 and score <= 100
pero más legible.
Es una característica exclusiva de Python
que no existe en la mayoría de lenguajes —
aparece frecuentemente en código generado por IA.
score = 85
# Comparación encadenada — pythónico
if 90 <= score <= 100:
print("Sobresaliente")
elif 70 <= score < 90:
print("Notable")
elif 50 <= score < 70:
print("Aprobado")
else:
print("Suspenso")
# Verificar rango de edad
edad = 25
if 18 <= edad <= 65:
print("En edad laboral activa")
# Verificar si un valor está en rango de datos válidos
temperatura = 36.8
if 35.0 <= temperatura <= 42.0:
print("Temperatura en rango fisiológico")
else:
print("Valor fuera de rango — revisar medición")
elif score >= 70
con score = 85,
¿qué sabe implícitamente sobre
el valor de score
que no necesita comprobar?
score = 90,
score = 69
y score = 50.
¿Coinciden los resultados?
and para
eliminar el anidamiento:
saldo = 500
importe = 200
if saldo > 0:
if saldo >= importe:
print("Transferencia aprobada")
else:
print("Saldo insuficiente")
else:
print("Cuenta en negativo")
imc = 27.5
if imc < 18.5:
print("Bajo peso")
elif imc < 25:
print("Peso normal")
elif imc < 30:
print("Sobrepeso")
else:
print("Obesidad")
Cuando Python llega al
elif score >= 70,
sabe implícitamente que
score < 90 —
porque si fuera 90 o más,
habría entrado en el
if score >= 90
y no habría llegado hasta aquí.
Por eso no es necesario escribir
score >= 70 and score < 90
— la condición del límite superior
está implícita en la estructura
del elif.
Sí, los resultados coinciden
en todos los casos — ambas versiones
son lógicamente equivalentes.
Con score = 90:
"Sobresaliente".
Con score = 69:
"Aprobado".
Con score = 50:
"Aprobado".
and explícito
puede ser más clara cuando
los rangos no son contiguos
o cuando el código será leído
por alguien sin experiencia en Python.
saldo = 500
importe = 200
if saldo > 0 and saldo >= importe:
print("Transferencia aprobada")
elif saldo > 0:
print("Saldo insuficiente")
else:
print("Cuenta en negativo")
La versión plana es más fácil
de leer de un vistazo — el flujo
es lineal sin saltos de indentación.
Nótese que saldo > 0 and saldo >= importe
podría simplificarse a
saldo >= importe
si el importe siempre es positivo,
porque un saldo suficiente para cubrir
el importe implica que es mayor que 0.
El código es correcto — el orden
de condiciones es el adecuado:
de menor a mayor, usando
< como frontera.
Con imc = 27.5:
27.5 < 18.5 es False,
27.5 < 25 es False,
27.5 < 30 es True
→ imprime "Sobrepeso".
La lógica es correcta y
el orden es el esperado —
este es un ejemplo de código
generado por IA que está bien escrito.
<
como operador, el orden correcto
es de menor a mayor —
lo opuesto al caso con >.
Con > hay que ir
de mayor a menor.
Con < hay que ir
de menor a mayor.
Una condición en un if es
cualquier expresión que devuelva
True o False.
Los operadores de comparación
son la forma más directa de construir
esas expresiones — comparan dos valores
y producen un booleano.
— Los seis operadores de comparación —
| Operador | Significado | Ejemplo | Resultado |
|---|---|---|---|
>
|
Mayor que |
5 > 3
|
True
|
<
|
Menor que |
2 < 1
|
False
|
>=
|
Mayor o igual que |
5 >= 5
|
True
|
<=
|
Menor o igual que |
3 <= 2
|
False
|
==
|
Igual a |
4 == 4
|
True
|
!=
|
Distinto de |
4 != 5
|
True
|
— La trampa más frecuente: = vs == —
= es asignación — le da un valor
a una variable. == es comparación
— pregunta si dos valores son iguales.
Confundirlos es uno de los errores más
frecuentes en Python, especialmente
en código generado por IA o copiado
de otro lenguaje.
# = asigna — no produce True/False
x = 10 # x ahora vale 10
# == compara — produce True/False
print(x == 10) # True
print(x == 20) # False
# Error típico: usar = en lugar de == en un if
if x == 10: # correcto
print("x vale 10")
# Verificar igualdad entre strings
nombre = "Ana"
if nombre == "Ana":
print("Hola Ana")
if nombre == "ana": # sensible a mayúsculas
print("esto no se imprime")
# Solución: normalizar antes de comparar
if nombre.lower() == "ana":
print("Hola Ana — versión insensible a mayúsculas")
— Comparaciones entre strings: orden lexicográfico —
Los operadores >, <,
>=, <=
también funcionan con strings — comparan
carácter a carácter según el código Unicode.
Las mayúsculas van antes que las minúsculas,
los números antes que las letras.
Esto aparece en código de ordenación
y validación de datos.
# Orden lexicográfico — como en un diccionario
print("ana" < "bob") # True — 'a' < 'b'
print("banana" < "manzana") # True — 'b' < 'm'
print("Ana" < "ana") # True — mayúscula < minúscula
print("10" < "9") # True — '1' < '9' como caracteres
# Atención: "10" como string vs 10 como número
print(10 < 9) # False — comparación numérica
print("10" < "9") # True — comparación lexicográfica
# Comparar tipos distintos lanza TypeError en Python 3
try:
print(10 < "9")
except TypeError as e:
print(f"Error: {e}")
print(3 == 3.0)
print(3 is 3.0)
print("Python" == "python")
print("Python".lower() == "python")
print(10 != 10.0)
"10" < "9"
es True pero
10 < 9 es False?
¿Qué consecuencia tiene esto
si comparas números que vienen
de un CSV cargado como strings?
umbral = 100
ventas = 150
if ventas = umbral:
print("ventas exactamente en umbral")
else:
print("ventas distintas del umbral")
precio_str = "99"
if precio_str > 50:
print("precio alto")
else:
print("precio bajo")
3 == 3.0 → True:
Python compara valores, no tipos —
el entero 3 y el float 3.0 son iguales.
3 is 3.0 → False:
son objetos distintos en memoria.
"Python" == "python"
→ False: sensible a mayúsculas.
"Python".lower() == "python"
→ True: normalizado.
10 != 10.0 → False:
son iguales en valor.
"10" < "9" compara
carácter a carácter:
'1' tiene código Unicode
menor que '9',
por eso es True.
10 < 9 compara valores
numéricos — es False.
La consecuencia práctica es grave:
si un CSV tiene una columna de precios
cargada como strings,
"100" < "20" es
True —
el filtrado producirá resultados
incorrectos sin ningún mensaje de error.
Siempre hay que convertir a número
antes de comparar:
int(precio_str) < 20.
ventas = umbral dentro
del if es un
SyntaxError —
Python no permite asignación
en una condición de esta forma.
El error es haber escrito
= en lugar de
==.
La corrección:
if ventas == umbral:
print("ventas exactamente en umbral")
else:
print("ventas distintas del umbral")
SyntaxError visible —
Python detecta que una asignación
no es válida como condición.
Es uno de los pocos casos donde
el error de = vs
== no es silencioso.
Produce un TypeError:
no se pueden comparar un string
y un entero con >.
La corrección es convertir
el string a número antes de comparar:
precio = int(precio_str) # o float() si tiene decimales
if precio > 50:
print("precio alto")
else:
print("precio bajo")
and,
or,
not:
combinar condiciones
práctica
En la Clase 2 vimos and,
or y not
como operadores sobre booleanos.
Ahora los usamos en su contexto natural:
combinar varias condiciones en un
mismo if para expresar
decisiones más complejas.
La clave para leer código con condiciones combinadas es entender el cortocircuito: Python no evalúa más condiciones de las necesarias. En cuanto conoce el resultado, se detiene.
— Los tres operadores lógicos —
| Operador | Resultado True si... | Cortocircuito |
|---|---|---|
and
|
Ambas condiciones son True | Para en el primer False |
or
|
Al menos una es True | Para en el primer True |
not
|
La condición es False | Sin cortocircuito — invierte |
— and: ambas condiciones deben ser True —
# Validar que un dato está en rango
edad = 25
if edad >= 18 and edad <= 65:
print("En edad laboral activa")
# Equivalente con comparación encadenada
if 18 <= edad <= 65:
print("En edad laboral activa")
# Validar múltiples campos de un registro
nombre = "Ana"
nota = 8.5
activo = True
if nombre != "" and nota >= 0 and activo:
print(f"Registro válido: {nombre}, {nota}")
# Cortocircuito de and: si la primera es False,
# la segunda no se evalúa
x = 0
if x != 0 and 100 / x > 10: # sin cortocircuito: ZeroDivisionError
print("condición cumplida")
print("no hay error — cortocircuito evitó la división")
— or: basta con que una sea True —
# Aceptar múltiples valores válidos
extension = "csv"
if extension == "csv" or extension == "xlsx" or extension == "json":
print("formato de datos reconocido")
# Más conciso con in
if extension in {"csv", "xlsx", "json"}:
print("formato de datos reconocido")
# Condición alternativa — si falla el dato principal
valor_principal = None
valor_backup = 42
if valor_principal or valor_backup:
print("al menos un valor disponible")
# Cortocircuito de or: si la primera es True,
# la segunda no se evalúa
datos = [1, 2, 3]
if len(datos) > 0 or sum(datos) > 100: # sum no se evalúa
print("condición cumplida")
— not: invertir una condición —
# not invierte el valor booleano
activo = False
if not activo:
print("usuario inactivo — enviar recordatorio")
# not in — muy frecuente para verificar ausencia
categorias_validas = {"A", "B", "C"}
categoria = "D"
if categoria not in categorias_validas:
print(f"Categoría '{categoria}' no reconocida")
# not con is None — verificar valor faltante
valor = None
if not valor is None: # funciona pero es menos pythónico
print("tiene valor")
if valor is not None: # forma pythónica — más legible
print("tiene valor")
# Precedencia: not > and > or
x = True
y = False
print(not x or y) # (not x) or y → False or False → False
print(not (x or y)) # not (True or False) → not True → False
El orden de precedencia de los operadores lógicos
es: not primero,
luego and,
luego or.
Cuando no estés seguro, usa paréntesis —
hacen explícito el orden de evaluación
y el código es más fácil de leer.
a = 5
b = 10
c = 15
print(a < b and b < c)
print(a > b or c > b)
print(not a == b)
print(a < b and b > c or c == 15)
print(a < b and (b > c or c == 15))
and
evita el ZeroDivisionError
en el ejemplo. Explica por qué:
si x = 0, ¿qué hace Python
cuando evalúa
x != 0 and 100 / x > 10?
¿Qué pasaría sin el cortocircuito?
precio = -5
stock = 0
nombre = "Manzana"
if precio > 0 and stock >= 0 and nombre != "":
print("registro válido")
elif precio <= 0:
print("error: precio no puede ser negativo o cero")
elif stock < 0:
print("error: stock no puede ser negativo")
else:
print("error: nombre vacío")
or
de forma más concisa usando in:
dia = "sábado"
if dia == "sábado" or dia == "domingo":
print("fin de semana")
else:
print("día laborable")
¿Qué ventaja tiene la versión
con in cuando
hay muchos valores posibles?
a < b and b < c
→ True: ambas son verdaderas.
a > b or c > b
→ True: la segunda es verdadera.
not a == b
→ True: 5 == 10
es False, negado es True.
a < b and b > c or c == 15
→ True: se evalúa como
(a < b and b > c) or c == 15
→ False or True
→ True.
a < b and (b > c or c == 15)
→ True: True and True.
Con x = 0,
Python evalúa x != 0
primero — es False.
Como es and,
si la primera condición es False
el resultado no puede ser True
sin importar la segunda.
Python se detiene y no evalúa
100 / x — el
ZeroDivisionError
nunca ocurre.
Sin cortocircuito, Python evaluaría
las dos condiciones siempre
y la división por cero detendría
el programa con un error.
x != 0) antes
de la operación peligrosa
(100 / x) es
un patrón frecuente para evitar
errores en análisis de datos —
verificar que el denominador
no es cero antes de dividir,
que la lista no está vacía
antes de acceder al primer elemento.
Con precio = -5:
la primera condición
precio > 0
es False —
el and cortocircuita
y no evalúa el resto.
El bloque del if
no se ejecuta.
El primer elif
— precio <= 0
es True — imprime
"error: precio no puede
ser negativo o cero".
dia = "sábado"
if dia in {"sábado", "domingo"}:
print("fin de semana")
else:
print("día laborable")
La ventaja de in
escala mucho mejor — si tuvieras
diez valores posibles,
la versión con or
requeriría diez comparaciones
explícitas. Con in
solo cambias el contenido
del set. Además la verificación
de pertenencia en un set
es O(1) — independientemente
de cuántos valores tenga.
in
en condicionales
práctica
En Python no hace falta comparar explícitamente
con True o False
para usar algo como condición.
Cualquier objeto puede usarse directamente
en un if — Python lo convierte
implícitamente a booleano.
Los objetos que se comportan como
True se llaman
truthy.
Los que se comportan como False
se llaman falsy.
Conocerlos permite escribir condiciones
más concisas y leer código ajeno
sin confundirse.
— Valores falsy en Python —
| Valor | Tipo | Significado |
|---|---|---|
False
|
bool | el propio False |
None
|
NoneType | ausencia de valor |
0, 0.0
|
int, float | cero numérico |
""
|
str | string vacío |
[], (),
{}, set()
|
list, tuple, dict, set | colección vacía |
Todo lo demás es truthy:
cualquier número distinto de cero,
cualquier string no vacío,
cualquier colección con al menos un elemento.
Puedes verificarlo con bool(valor).
— Truthy y falsy en condicionales —
# Verificar si una lista tiene elementos
datos = [1, 2, 3]
vacia = []
if datos: # truthy — tiene elementos
print("hay datos")
if not vacia: # falsy negado — vacía
print("lista vacía")
# Equivalente explícito — más verboso, mismo resultado
if len(datos) > 0:
print("hay datos")
# Verificar si un string tiene contenido
nombre = ""
if nombre:
print(f"nombre: {nombre}")
else:
print("nombre vacío — campo obligatorio")
# Verificar si un valor es None
resultado = None
if resultado is None: # forma correcta para None
print("sin resultado todavía")
if resultado is not None: # forma correcta para no-None
print("resultado disponible")
# Verificar con bool() explícito
print(bool([])) # False
print(bool([1])) # True
print(bool("")) # False
print(bool("0")) # True — string no vacío
print(bool(0)) # False
print(bool(0.0)) # False
— in y not in: pertenencia como condición —
El operador in — que vimos
en la Clase 3 para verificar pertenencia
en listas, dicts y sets — es también
una de las condiciones más frecuentes
en condicionales. Permite expresar
de forma natural preguntas como
"¿este valor está en la colección?"
o "¿esta clave existe en el diccionario?".
# in con listas — O(n)
frutas = ["manzana", "pera", "naranja"]
if "pera" in frutas:
print("pera disponible")
# in con sets — O(1) más eficiente
categorias_validas = {"A", "B", "C"}
categoria = "D"
if categoria not in categorias_validas:
print(f"categoría '{categoria}' no válida")
# in con diccionarios — verifica claves
alumno = {"nombre": "Ana", "nota": 8.5}
if "nota" in alumno:
print(f"nota: {alumno['nota']}")
if "edad" not in alumno:
print("campo edad ausente")
# in con strings — busca subcadena
email = "ana@uvigo.es"
if "@" in email:
print("formato de email válido")
if "uvigo" in email:
print("email institucional")
— Patrones frecuentes en análisis de datos —
import pandas as pd
df = pd.DataFrame({
"producto": ["manzana", "pera", None, "naranja"],
"ventas": [150, 0, 80, 120],
"activo": [True, False, True, True]
})
# Patrón 1: verificar si una columna existe
if "ventas" in df.columns:
print(f"total ventas: {df['ventas'].sum()}")
# Patrón 2: verificar si el DataFrame tiene datos
if not df.empty:
print(f"filas disponibles: {len(df)}")
# Patrón 3: verificar valores nulos antes de operar
valor = df["producto"].iloc[2] # None
if valor is not None:
print(f"producto: {valor.upper()}")
else:
print("valor faltante — omitir fila")
bool() antes de ejecutar:
print(bool(0))
print(bool("False"))
print(bool([]))
print(bool([None]))
print(bool({}))
print(bool({0: "cero"}))
valor = 0
if valor:
print("versión truthy")
if valor is not None:
print("versión is not None")
datos = []
config = {"umbral": 0}
if datos and "umbral" in config and config["umbral"]:
print("procesando")
elif not datos:
print("sin datos")
elif not config["umbral"]:
print("umbral en cero")
else:
print("otro error")
if len(lista) > 0:
...
if nombre != "":
...
if valor == None:
...
if activo == True:
...
bool(0) → False:
cero es falsy.
bool("False") → True:
string no vacío — aunque diga "False",
es truthy.
bool([]) → False:
lista vacía.
bool([None]) → True:
lista con un elemento — aunque sea
None, la lista no está vacía.
bool({}) → False:
diccionario vacío.
bool({0: "cero"}) → True:
diccionario con un elemento.
bool("False") es
True — el string
"False" no es el
booleano False.
Si necesitas convertir el string
"False" al booleano
False, no puedes
usar bool() directamente.
Con valor = 0:
la versión truthy no imprime nada
— 0 es falsy.
La versión is not None
sí imprime — 0 no es
None.
La diferencia importa cuando
0 es un valor válido
en tus datos — por ejemplo,
ventas de cero unidades.
Si usas la versión truthy,
tratarás el cero como "sin datos"
cuando en realidad es un dato válido.
En ese caso la forma correcta
es valor is not None.
if valor)
cuando quieras excluir todos los
valores vacíos o cero.
Usa is not None
cuando el cero o el string vacío
son valores legítimos y solo
quieres excluir la ausencia de dato.
datos es una lista vacía
— falsy. La primera condición del
and es False
— cortocircuita y no evalúa el resto.
El bloque if no se ejecuta.
El primer elif not datos
es True — imprime
"sin datos".
El elif not config["umbral"]
nunca se alcanza en este caso.
Si datos = [1, 2, 3],
entonces pasaría al siguiente
elif porque
config["umbral"] es
0 — falsy —
e imprimiría "umbral en cero".
if lista: # en lugar de len(lista) > 0
...
if nombre: # en lugar de nombre != ""
...
if valor is None: # en lugar de valor == None
...
if activo: # en lugar de activo == True
...
is None
en lugar de == None:
None es un singleton —
existe un único objeto None
en Python. La comparación correcta
es de identidad (is),
no de igualdad (==).
Algunos linters como flake8
advierten si usas == None
en lugar de is None.
Las estructuras de datos de la Clase 3
— listas, tuplas, diccionarios y sets —
aparecen constantemente como condiciones
en un if. Conocer los patrones
más frecuentes permite leer código ajeno
sin confusión y detectar errores
antes de ejecutar.
El patrón más importante es el patrón de guardia: verificar que una estructura tiene lo que necesitas antes de operar sobre ella. Sin esta verificación, operaciones como acceder al primer elemento de una lista vacía o buscar una clave inexistente en un dict producen errores en tiempo de ejecución.
— Condicionales sobre listas —
ventas = [150, 80, 200, 95]
vacia = []
# Patrón de guardia: verificar antes de operar
if ventas:
print(f"primera venta: {ventas[0]}") # seguro
print(f"media: {sum(ventas)/len(ventas):.1f}")
# Sin guardia — IndexError si la lista está vacía
try:
print(vacia[0])
except IndexError as e:
print(f"IndexError: {e}")
# Verificar pertenencia
productos_activos = ["manzana", "pera", "naranja"]
pedido = "kiwi"
if pedido in productos_activos:
print(f"procesando pedido de {pedido}")
else:
print(f"{pedido} no disponible")
# Verificar longitud mínima
registros = ["Ana", "Bob"]
if len(registros) >= 3:
print("suficientes registros para analizar")
else:
print(f"solo {len(registros)} registro/s — se necesitan al menos 3")
— Condicionales sobre diccionarios —
alumno = {"nombre": "Ana", "nota": 8.5, "activo": True}
incompleto = {"nombre": "Bob"}
# Patrón de guardia: verificar clave antes de acceder
if "nota" in alumno:
print(f"nota: {alumno['nota']}") # seguro
# Sin guardia — KeyError
try:
print(incompleto["nota"])
except KeyError as e:
print(f"KeyError: {e}")
# Verificar si el dict tiene datos
config = {}
if config:
print("configuración cargada")
else:
print("usando configuración por defecto")
# Verificar valor dentro del dict
if "activo" in alumno and alumno["activo"]:
print(f"{alumno['nombre']} está activo")
# Alternativa con get() — sin necesidad de guardia
nota = alumno.get("nota")
if nota is not None:
print(f"nota disponible: {nota}")
— Condicionales sobre sets —
permisos_usuario = {"leer", "escribir"}
permisos_admin = {"leer", "escribir", "borrar", "admin"}
accion_solicitada = "borrar"
# Verificar permiso específico
if accion_solicitada in permisos_usuario:
print("acción permitida")
else:
print(f"sin permiso para '{accion_solicitada}'")
# Verificar si tiene todos los permisos necesarios
permisos_necesarios = {"leer", "escribir"}
if permisos_necesarios.issubset(permisos_usuario):
print("tiene todos los permisos requeridos")
# Verificar solapamiento entre dos grupos
clientes_enero = {"Ana", "Bob", "Carlos"}
clientes_febrero = {"Bob", "Diana", "Eva"}
if clientes_enero & clientes_febrero:
print("hay clientes en común")
print(f"clientes fieles: {clientes_enero & clientes_febrero}")
— Condicionales sobre tuplas —
# Tupla como registro — verificar campos
registro = ("Ana", 8.5, True)
vacio = ()
if registro:
nombre, nota, activo = registro # unpacking seguro
if activo and nota >= 5:
print(f"{nombre}: aprobado ({nota})")
# Verificar longitud antes de desempaquetar
coordenadas = (40.4168, -3.7038)
if len(coordenadas) == 2:
lat, lon = coordenadas
print(f"latitud: {lat}, longitud: {lon}")
# in con tupla — pertenencia
dias_laborables = ("lunes", "martes", "miércoles",
"jueves", "viernes")
hoy = "sábado"
if hoy not in dias_laborables:
print("hoy no hay clase")
inventario = {
"manzana": 50,
"pera": 0,
"naranja": 30
}
pedido = "pera"
if pedido in inventario and inventario[pedido] > 0:
print(f"enviando {pedido}")
elif pedido in inventario:
print(f"{pedido} agotado")
else:
print(f"{pedido} no existe")
# Forma A
if "nota" in alumno:
nota = alumno["nota"]
# Forma B
nota = alumno.get("nota")
if nota is not None:
...
registros = [
{"nombre": "Ana", "nota": 8.5},
{"nombre": "Bob"},
{"nombre": "Carlos", "nota": 6.0}
]
mejor = registros[0]
for r in registros:
if r["nota"] > mejor["nota"]:
mejor = r
print(f"mejor alumno: {mejor['nombre']}")
Si la lista está vacía,
acceder a lista[0]
produce un IndexError
que detiene el programa.
La forma más concisa de
escribir la guardia es:
if lista:
print(lista[0])
Aprovecha que una lista vacía
es falsy — es más conciso
que if len(lista) > 0
y produce exactamente el mismo resultado.
pedido = "pera":
"pera" in inventario
es True.
inventario["pera"] > 0
es False — el stock es 0.
El primer bloque no se ejecuta.
El elif —
"pera" in inventario
es True — imprime
"pera agotado".
"pera" in inventario and inventario["pera"] > 0
es seguro porque el cortocircuito
del and garantiza que
inventario["pera"]
solo se evalúa si la clave existe.
Sin la guardia, acceder a una clave
inexistente produciría un
KeyError.
La Forma A es más explícita — primero verifica, luego accede. Útil cuando quieres distinguir entre "la clave no existe" y "la clave existe pero vale None".
La Forma B es más compacta —
usa get() que devuelve
None si la clave no existe,
y luego verifica ese None.
Pero si el valor legítimo del campo
puede ser None,
no distingue entre "clave ausente"
y "clave presente con valor None".
En ese caso la Forma A es más segura.
Dos problemas: primero,
registros[0] falla
si la lista está vacía.
Segundo, r["nota"]
lanza KeyError
en el registro de Bob
que no tiene la clave "nota".
La versión corregida:
if not registros:
print("sin registros")
else:
# solo comparar registros que tienen nota
con_nota = [r for r in registros if "nota" in r]
if con_nota:
mejor = max(con_nota, key=lambda r: r["nota"])
print(f"mejor alumno: {mejor['nombre']}")
pandas es la librería central del análisis de datos en Python. Su estructura principal es el DataFrame — una tabla con filas y columnas, similar a una hoja de cálculo o a una tabla de base de datos. Cada columna es una Series — una lista con índice y nombre.
No vamos a estudiar pandas en detalle
hasta clases posteriores. Pero hay
un concepto que conecta directamente
con los condicionales de hoy:
el filtrado booleano.
En pandas, filtrar filas es exactamente
lo mismo que un if de Python
— pero aplicado a toda una columna
a la vez en lugar de a un solo valor.
— Un DataFrame: la tabla de datos de pandas —
import pandas as pd
# Crear un DataFrame desde un diccionario
df = pd.DataFrame({
"producto": ["manzana", "pera", "naranja", "kiwi", "fresa"],
"ventas": [150, 80, 120, 0, 200],
"precio": [1.20, 0.90, 0.80, 1.50, 2.00],
"categoria": ["fruta", "fruta", "cítrico", "exótico", "fruta"],
"activo": [True, True, True, False, True]
})
print(df)
print()
print(f"Filas: {len(df)}, Columnas: {len(df.columns)}")
print(f"Columnas: {list(df.columns)}")
— if de Python vs filtrado booleano en pandas —
En Python un if evalúa
una condición sobre un valor
y ejecuta un bloque o no.
En pandas, el filtrado booleano evalúa
una condición sobre toda una columna
a la vez — devuelve una Serie de
True/False
que selecciona las filas que cumplen
la condición.
La idea es exactamente la misma —
solo cambia la escala.
# Python: if sobre un valor
ventas_valor = 150
if ventas_valor > 100:
print("ventas altas")
# pandas: condición sobre toda la columna
mascara = df["ventas"] > 100
print(mascara) # Serie de True/False
print(type(mascara))
# Usar la máscara para filtrar filas
df_altas = df[mascara]
print(df_altas)
# En una sola línea — forma más frecuente
df_altas = df[df["ventas"] > 100]
print(df_altas[["producto", "ventas"]])
— Filtros combinados: and, or, not en pandas —
En pandas no se usan and,
or y not
para combinar condiciones —
se usan los operadores
&, | y ~.
Y cada condición debe ir entre paréntesis.
Es uno de los errores más frecuentes
en código generado por IA: usar
and en lugar de &
produce un error críptico.
# Python: and, or, not
if ventas_valor > 100 and ventas_valor < 200:
print("rango medio")
# pandas: & | ~ con paréntesis obligatorios
# & equivale a and
df_rango = df[(df["ventas"] > 100) & (df["ventas"] < 200)]
print(df_rango[["producto", "ventas"]])
# | equivale a or
df_extremos = df[(df["ventas"] == 0) | (df["ventas"] >= 200)]
print(df_extremos[["producto", "ventas"]])
# ~ equivale a not
df_activos = df[df["activo"]] # activos
df_inactivos = df[~df["activo"]] # inactivos — ~ invierte
print(df_inactivos[["producto", "activo"]])
# Error frecuente: usar and en lugar de &
try:
df_error = df[df["ventas"] > 100 and df["activo"]]
except ValueError as e:
print(f"Error: {e}")
— Validación del DataFrame con if de Python —
El if de Python sigue siendo
necesario en pandas para verificar el estado
del DataFrame antes de operar —
si está vacío, si tiene las columnas que
necesitas, si hay valores nulos.
Son los patrones de guardia de la Clase 3
aplicados a DataFrames.
# Patrón 1: verificar si el DataFrame tiene datos
if df.empty:
print("DataFrame vacío — nada que procesar")
else:
print(f"procesando {len(df)} filas")
# Patrón 2: verificar si una columna existe
if "ventas" in df.columns:
print(f"total ventas: {df['ventas'].sum()}")
else:
print("columna 'ventas' no encontrada")
# Patrón 3: verificar si hay valores nulos
df_con_nulos = df.copy()
df_con_nulos.loc[1, "ventas"] = None
if df_con_nulos["ventas"].isnull().any():
print("hay valores nulos en ventas — revisar datos")
nulos = df_con_nulos["ventas"].isnull().sum()
print(f" filas con nulo: {nulos}")
# Patrón 4: verificar resultado del filtrado
df_filtrado = df[df["categoria"] == "exótico"]
if df_filtrado.empty:
print("ningún producto de la categoría solicitada")
else:
print(f"encontrados: {len(df_filtrado)} productos")
if de Python
y el filtrado booleano de pandas?
¿Qué devuelve
df["ventas"] > 100?
¿Y df[df["ventas"] > 100]?
df[(df["precio"] < 1.0) & (df["ventas"] > 50)]
df[(df["categoria"] == "fruta") | (df["ventas"] == 0)]
df[~df["activo"]]
& en lugar de and
para combinar condiciones?
¿Qué error produce usar and?
df_validos = df[(df["activo"]) & (df["ventas"] > 0)]
media = df_validos["precio"].mean()
print(f"precio medio: {media:.2f}€")
El if de Python
evalúa una condición sobre
un único valor y decide si ejecutar
un bloque o no.
El filtrado booleano de pandas
evalúa una condición sobre
toda una columna a la vez.
df["ventas"] > 100
devuelve una Serie booleana
— una columna de True
y False para cada fila.
df[df["ventas"] > 100]
usa esa Serie como máscara y devuelve
un DataFrame con solo
las filas donde el valor es
True.
Primer filtro —
precio menor que 1.0 and
ventas mayor que 50:
pera (0.90, 80) y naranja (0.80, 120).
Segundo filtro —
categoría fruta or
ventas igual a 0:
manzana, pera, fresa (frutas)
y kiwi (ventas = 0).
Tercer filtro —
not activo:
solo kiwi, que tiene
activo = False.
and en Python
opera sobre valores escalares
— devuelve uno de los dos operandos.
En pandas, la condición es una Serie
de muchos valores — Python no sabe
cómo aplicar and
a una Serie entera y lanza un
ValueError:
"The truth value of a Series
is ambiguous".
Con & pandas aplica
la operación elemento a elemento
sobre toda la Serie — que es
exactamente lo que queremos.
and
en lugar de &.
El mensaje de error
"The truth value of a Series
is ambiguous" es la señal
inequívoca de este problema.
El código es correcto en lógica
pero puede fallar si
df_validos está vacío —
mean() sobre un DataFrame
vacío devuelve NaN,
no un error, pero el
:.2f del f-string
puede producir resultados confusos.
La versión con guardia:
df_validos = df[(df["activo"]) & (df["ventas"] > 0)]
if df_validos.empty:
print("sin productos válidos para calcular media")
else:
media = df_validos["precio"].mean()
print(f"precio medio: {media:.2f}€")
match
/
case:
múltiples opciones de forma limpia
teoría
Introducido en Python 3.10,
match/case es una forma
más limpia de manejar múltiples opciones
cuando comparas un valor contra
un conjunto fijo de posibilidades.
Es similar al switch
de otros lenguajes como Java o JavaScript.
No reemplaza al if/elif/else
en general — es específico para el patrón
"comparar una variable contra varios
valores concretos". Cuando la IA genera
código con este patrón, casi siempre
usa match/case si detecta
que estás comparando contra valores fijos.
Saber leerlo es imprescindible.
— Sintaxis básica: elif largo vs match/case —
# Con elif — funciona pero repetitivo
pais = "Francia"
if pais == "España":
print("Idioma: Español")
elif pais == "Francia":
print("Idioma: Francés")
elif pais == "Alemania":
print("Idioma: Alemán")
else:
print("Idioma desconocido")
# Con match/case — más limpio
match pais:
case "España":
print("Idioma: Español")
case "Francia":
print("Idioma: Francés")
case "Alemania":
print("Idioma: Alemán")
case _:
print("Idioma desconocido")
La estructura es: match seguido
del valor a comparar, y luego un bloque
con los case.
Cada case compara el valor
con un patrón — si coincide, ejecuta
el bloque y sale del match.
El case _ es el caso por defecto
— equivale al else y captura
todo lo que no coincidió con ningún caso anterior.
El guión bajo _ es el comodín
de Python — significa "cualquier cosa".
— Agrupar casos con | —
Varios valores que producen el mismo resultado
pueden agruparse en un único case
usando el operador |
— igual que el or de los condicionales
pero dentro de un case.
Evita repetir el mismo bloque
para cada valor.
pais = "México"
match pais:
case "España" | "México" | "Argentina" | "Colombia":
print("Idioma: Español")
case "Francia" | "Bélgica" | "Suiza":
print("Idioma: Francés")
case "Alemania" | "Austria":
print("Idioma: Alemán")
case _:
print("Idioma desconocido")
— match/case con distintos tipos de valor —
# Con números — clasificar códigos de estado HTTP
codigo = 404
match codigo:
case 200:
print("OK — solicitud exitosa")
case 201:
print("Creado — recurso nuevo")
case 400:
print("Bad Request — error del cliente")
case 401 | 403:
print("Sin autorización o prohibido")
case 404:
print("Not Found — recurso no existe")
case 500 | 502 | 503:
print("Error del servidor")
case _:
print(f"Código {codigo} no reconocido")
# Con strings — clasificar extensiones de archivo
extension = "xlsx"
match extension.lower():
case "csv" | "tsv":
print("archivo de texto delimitado")
case "xlsx" | "xls":
print("libro de Excel")
case "json" | "jsonl":
print("archivo JSON")
case "parquet" | "feather":
print("formato columnar — pandas nativo")
case _:
print(f"extensión '.{extension}' no reconocida")
— ¿Cuándo usar match/case y cuándo if/elif? —
Usa match/case cuando...
Comparas una variable
contra valores fijos concretos
— strings, números, constantes.
Hay tres o más casos
con el mismo patrón de comparación.
Quieres agrupar varios valores
que producen el mismo resultado.
Usa if/elif cuando...
Las condiciones son
rangos numéricos
o expresiones complejas.
Combinas condiciones con
and, or.
Solo hay dos casos
— un simple if/else es más claro.
case _
en un bloque match?
¿Es obligatorio? ¿Qué pasa si
el valor no coincide con ningún
case y no hay
case _?
codigo = 403
y con codigo = 301:
match codigo:
case 200 | 201 | 204:
print("éxito")
case 301 | 302:
print("redirección")
case 401 | 403:
print("acceso denegado")
case 404:
print("no encontrado")
case _:
print("otro código")
elif como
match/case:
rol = "editor"
if rol == "admin":
print("acceso total")
elif rol == "editor" or rol == "moderador":
print("acceso parcial")
elif rol == "lector":
print("solo lectura")
else:
print("rol no reconocido")
match/case
no es adecuado para clasificar
un IMC en rangos numéricos?
¿Qué estructura usarías en su lugar
y por qué?
case _ es el caso
por defecto — captura cualquier valor
que no coincidió con ningún
case anterior.
No es obligatorio.
Si el valor no coincide con ningún
case y no hay
case _, Python
simplemente no ejecuta ningún bloque
y continúa — sin error, sin aviso.
Es recomendable incluirlo siempre
para manejar valores inesperados.
Con codigo = 403:
coincide con
case 401 | 403
→ imprime "acceso denegado".
Con codigo = 301:
coincide con
case 301 | 302
→ imprime "redirección".
rol = "editor"
match rol:
case "admin":
print("acceso total")
case "editor" | "moderador":
print("acceso parcial")
case "lector":
print("solo lectura")
case _:
print("rol no reconocido")
La versión match/case
es más limpia — elimina la repetición
de rol == en cada línea
y el or se convierte
en el más conciso |.
match/case compara
contra valores fijos concretos —
no puede expresar rangos como
imc < 18.5
o 18.5 <= imc < 25.
Para rangos numéricos la estructura
correcta es if/elif/else
con operadores de comparación,
ordenando las condiciones de más
restrictivo a menos restrictivo
como vimos en la Parte II.
match/case para
valores discretos y fijos.
if/elif/else para
rangos, condiciones compuestas
o cualquier expresión que no sea
una comparación de igualdad simple.
if/else
en una línea
práctica
El operador ternario permite escribir
un if/else simple en una
sola línea cuando el resultado es
un valor — no una acción.
La sintaxis es:
valor_si_true
if
condicion
else
valor_si_false
El resultado de la expresión completa es uno de los dos valores — el que corresponde según la condición. Es especialmente frecuente en código generado por IA porque permite asignar valores condicionalmente de forma compacta.
— if/else normal vs operador ternario —
nota = 7.5
# if/else normal — cuatro líneas
if nota >= 5:
resultado = "aprobado"
else:
resultado = "suspenso"
print(resultado)
# Operador ternario — una línea
resultado = "aprobado" if nota >= 5 else "suspenso"
print(resultado)
# Directamente en un print o f-string
print("aprobado" if nota >= 5 else "suspenso")
print(f"Nota {nota}: {'aprobado' if nota >= 5 else 'suspenso'}")
— Casos de uso frecuentes —
# Asignar valor por defecto si None
nombre = None
nombre = nombre if nombre is not None else "Sin nombre"
print(nombre)
# Seleccionar entre dos textos según condición
stock = 0
estado = "disponible" if stock > 0 else "agotado"
print(f"Producto: {estado}")
# Seleccionar entre dos valores numéricos
precio_base = 100
es_socio = True
precio_final = precio_base * 0.8 if es_socio else precio_base
print(f"Precio final: {precio_final}€")
# En una lista — etiquetar datos
ventas = [150, 80, 200, 30, 120]
etiquetas = ["alta" if v > 100 else "baja" for v in ventas]
print(etiquetas)
— Cuándo NO usar el operador ternario —
El ternario es útil cuando la condición es simple y el resultado es un valor. Se convierte en un problema cuando la condición es compleja, cuando hay más de dos casos o cuando se anidan varios ternarios. En esos casos el código se vuelve ilegible — exactamente lo contrario de lo que queremos.
# ✓ Ternario apropiado — simple y legible
signo = "positivo" if x > 0 else "no positivo"
# ❌ Ternario inapropiado — difícil de leer
# Ternarios anidados: evitar siempre
x = 5
categoria = "alto" if x > 8 else "medio" if x > 4 else "bajo"
print(categoria) # funciona, pero nadie lo entiende a primera vista
# ✓ La versión legible con if/elif/else
if x > 8:
categoria = "alto"
elif x > 4:
categoria = "medio"
else:
categoria = "bajo"
# ❌ Ternario con acciones — no es para esto
# Cuando el resultado no es un valor sino una acción
# usar if/else normal es más claro
print("ok") if x > 0 else print("error") # funciona, pero es feo
— El ternario vectorizado en pandas: np.where() —
En pandas, el equivalente vectorizado
del operador ternario es
np.where(condición, valor_si_true,
valor_si_false).
Aplica la lógica ternaria a toda
una columna a la vez —
igual que el filtrado booleano
es el if de pandas,
np.where() es
el ternario de pandas.
import pandas as pd
import numpy as np
df = pd.DataFrame({
"producto": ["manzana", "pera", "naranja", "kiwi"],
"ventas": [150, 80, 200, 30],
"precio": [1.20, 0.90, 0.80, 1.50]
})
# Python: ternario sobre un valor
v = 150
etiqueta = "alta" if v > 100 else "baja"
# pandas: np.where() sobre toda la columna
df["rendimiento"] = np.where(df["ventas"] > 100, "alta", "baja")
print(df[["producto", "ventas", "rendimiento"]])
# Calcular precio con descuento condicionalmente
df["precio_final"] = np.where(
df["ventas"] > 100,
df["precio"] * 0.9, # 10% descuento si ventas altas
df["precio"] # precio normal si ventas bajas
)
print(df[["producto", "precio", "precio_final"]])
if/else como
operador ternario en una línea:
# A
if edad >= 18:
acceso = "permitido"
else:
acceso = "denegado"
# B
if temperatura > 37.5:
alerta = True
else:
alerta = False
resultado
en cada caso antes de ejecutar:
x = 0
resultado = "cero" if x == 0 else "positivo" if x > 0 else "negativo"
print(resultado)
x = 5
resultado = "cero" if x == 0 else "positivo" if x > 0 else "negativo"
print(resultado)
x = -3
resultado = "cero" if x == 0 else "positivo" if x > 0 else "negativo"
print(resultado)
¿Es legible este código?
¿Cómo lo reescribirías?
np.where() de pandas?
¿Cuándo usarías cada uno?
gasto = 450
segmento = "premium" if gasto >= 500 \
else "medio" if gasto >= 200 \
else "básico"
print(f"Segmento: {segmento}")
# A
acceso = "permitido" if edad >= 18 else "denegado"
# B — aún más conciso
alerta = temperatura > 37.5
# La comparación ya devuelve True/False directamente
# No hace falta el ternario en este caso
El caso B es interesante:
cuando el resultado del ternario
es True o False,
casi siempre puedes simplificarlo
a la comparación directa —
alerta = temperatura > 37.5
es equivalente y más conciso.
Con x = 0:
"cero".
Con x = 5:
"positivo".
Con x = -3:
"negativo".
El código funciona pero no es legible — hay que seguir el encadenamiento mentalmente para entenderlo. La versión más clara:
if x == 0:
resultado = "cero"
elif x > 0:
resultado = "positivo"
else:
resultado = "negativo"
if/elif/else.
El ternario anidado es uno de los
patrones más frecuentes en código
generado por IA que resulta
difícil de leer y verificar.
El ternario de Python opera
sobre un único valor escalar
— devuelve uno de los dos valores
según la condición.
np.where() opera
sobre una Serie o columna entera
— aplica la misma lógica
a cada elemento de la columna
y devuelve una Serie con los resultados.
Usa el ternario de Python
cuando trabajas con un valor simple.
Usa np.where()
cuando quieres crear una columna
nueva en un DataFrame basada
en una condición sobre otra columna.
El ternario anidado funciona
pero está en el límite de la legibilidad
— tres casos con ternarios encadenados
requieren esfuerzo para seguirlos.
Con gasto = 450:
450 >= 500 es
False → evalúa el segundo:
450 >= 200 es
True → resultado:
"medio".
Para este caso con tres segmentos
la versión más legible es
if/elif/else:
if gasto >= 500:
segmento = "premium"
elif gasto >= 200:
segmento = "medio"
else:
segmento = "básico"
if/elif/else
es siempre más claro.
Mismo esquema que la Clase 3: escribe el pseudocódigo, dáselo a la IA, lee el código que genera antes de ejecutarlo. Predice qué estructuras usará y por qué. Después verifica si tu predicción fue correcta y si el código tiene algún problema.
En esta clase el foco está en los condicionales —
presta atención especial al orden de los
elif, a los operadores
(> vs >=,
and vs or)
y a los casos límite.
Son exactamente los puntos donde
el código generado por IA falla
de forma silenciosa.
— Tarea 1 (obligatoria) —
Pseudocódigo — Clasificador de ventas
"""
Tengo una lista de productos. Cada producto
tiene nombre, ventas del mes y stock actual.
Quiero clasificar cada producto según estas reglas:
- Si el stock es 0: etiqueta "agotado"
independientemente de las ventas
- Si el stock > 0 y ventas >= 150: "alto rendimiento"
- Si el stock > 0 y ventas >= 80: "rendimiento medio"
- Si el stock > 0 y ventas < 80: "bajo rendimiento"
Para cada producto quiero imprimir:
nombre → ventas unidades → etiqueta
Los datos de ejemplo son:
manzana: ventas=200, stock=50
pera: ventas=85, stock=30
naranja: ventas=60, stock=0
kiwi: ventas=150, stock=20
fresa: ventas=40, stock=0
"""
Antes de darle este pseudocódigo a la IA, responde estas preguntas:
>= o >
para el umbral de 150 ventas?
¿Qué etiqueta recibirá el kiwi
con exactamente 150 ventas?Después de obtener el código, verifica: ¿puso la condición de stock antes que la de ventas? ¿Qué pasaría si las pusiera al revés? Prueba con naranja (ventas=60, stock=0) — ¿recibe la etiqueta correcta?
— Tarea 2 (obligatoria) —
Pseudocódigo — Validador de registros
"""
Tengo una lista de registros de alumnos del máster.
Cada registro tiene: nombre, nota y matricula_activa.
Quiero validar cada registro y mostrar un informe:
- Si el nombre está vacío: "error — nombre vacío"
- Si la nota está fuera del rango 0-10:
"error — nota fuera de rango"
- Si la matrícula no está activa:
"inactivo — no computa para el ranking"
- Si todo está bien y la nota >= 5: "aprobado"
- Si todo está bien y la nota < 5: "suspenso"
Cada error debe identificar el registro afectado.
Si un registro tiene varios errores, mostrar
solo el primero que se detecte.
Los datos de ejemplo son:
{"nombre": "Ana López", "nota": 8.5, "matricula_activa": True}
{"nombre": "", "nota": 7.0, "matricula_activa": True}
{"nombre": "Bob García", "nota": 11.0, "matricula_activa": True}
{"nombre": "Carlos Díaz", "nota": 6.5, "matricula_activa": False}
{"nombre": "Diana Ruiz", "nota": 4.5, "matricula_activa": True}
"""
Antes de darle este pseudocódigo a la IA, responde estas preguntas:
nombre == ""
o not nombre?
¿Son equivalentes?
or,
con not ... and
o con comparación encadenada?if/elif/else
o múltiples if
independientes?
¿Cuál es la diferencia?Después de obtener el código, verifica: ¿qué imprime para el registro vacío? ¿Y para Bob con nota 11? ¿Y para Carlos con matrícula inactiva? ¿Coincide con tu predicción?
— Tarea 3 (opcional) —
Pseudocódigo — Clasificador de archivos
"""
Tengo una lista de nombres de archivo.
Quiero clasificar cada archivo según su extensión:
- csv, tsv, xlsx, xls → "datos"
- jpg, jpeg, png, gif, svg → "imagen"
- pdf, docx, doc, txt, md → "documento"
- py, ipynb, r, sql → "código"
- cualquier otra extensión → "desconocido"
Para cada archivo quiero imprimir:
nombre_archivo → categoría
Debe funcionar aunque la extensión esté
en mayúsculas o minúsculas.
Los archivos de ejemplo son:
"ventas_2024.CSV", "informe.pdf",
"script.py", "foto_reunion.JPG",
"datos.parquet", "notas.DOCX",
"analisis.ipynb", "logo.svg"
"""
Antes de darle este pseudocódigo a la IA, responde estas preguntas:
match/case
o if/elif con in?
¿Cuál es más adecuado aquí?
Después de obtener el código, verifica:
¿clasifica correctamente "ventas_2024.CSV"
y "foto_reunion.JPG" aunque estén en mayúsculas?
¿Y "datos.parquet" que no está en ninguna categoría?
¿Usó match/case o if/elif
con in? ¿Cuál genera la IA
más frecuentemente?
— Tarea 4 (opcional) —
Pseudocódigo — Análisis de DataFrame con pandas
"""
Tengo un DataFrame de ventas mensuales con columnas:
producto, ventas, precio, categoria y activo.
Quiero hacer tres cosas:
1. Añadir una columna "rendimiento" con tres valores:
- "alto" si ventas >= 150
- "medio" si ventas >= 80
- "bajo" si ventas < 80
2. Añadir una columna "precio_ajustado":
- Si ventas >= 150: precio * 0.85 (descuento 15%)
- En caso contrario: precio sin cambio
3. Filtrar y mostrar solo los productos que:
- Están activos (activo == True)
- Son de categoría "fruta"
- Tienen ventas > 0
Los datos de ejemplo son:
producto ventas precio categoria activo
manzana 200 1.20 fruta True
pera 85 0.90 fruta True
naranja 60 0.80 cítrico True
kiwi 0 1.50 exótico False
fresa 180 2.00 fruta True
"""
Antes de darle este pseudocódigo a la IA, responde estas preguntas:
np.where() anidado
o alguna otra función de pandas?
np.where()?
¿Qué operador usará para
combinar las tres condiciones
del filtro — and
o &?
Después de obtener el código, verifica:
¿usó np.where() anidado
para tres categorías o encontró
otra solución? ¿El filtro usa
& correctamente
con paréntesis? ¿Qué productos
pasan el filtro?
Para todas las tareas:
antes de ejecutar, identifica en el código
generado: el orden de los elif,
los operadores de comparación en los límites
(> vs >=),
y si las condiciones compuestas usan
and/or
correctamente. Prueba siempre los valores
exactamente en el límite de cada condición.
A continuación te pongo un vídeo de una introducción práctica al llamado vibe
coding:
una forma de programar apoyándose continuamente en asistentes de IA
como ChatGPT, Claude o Cursor.
Más allá del tono informal, el vídeo muestra algo importante:
cómo organizar un flujo de trabajo real donde el programador no solo
escribe código, sino que también planifica, depura errores,
formula instrucciones precisas y supervisa lo que genera la IA.
Mientras lo ves, fíjate especialmente en esto:
¿qué tareas siguen requiriendo comprensión humana?
¿Dónde aparecen los errores?
¿Por qué el autor insiste continuamente en revisar,
depurar y entender el código generado?
El objetivo no es “dejar que la IA programe sola”,
sino aprender a trabajar con ella de forma crítica y productiva.
— Tarea 1: Clasificador de ventas —
La condición de stock debe ir primera — es la más restrictiva y excluye completamente la lógica de ventas. Si la condición de ventas fuera primera, naranja (ventas=60, stock=0) recibiría "bajo rendimiento" en lugar de "agotado". El orden correcto es: primero la excepción (stock = 0), luego las reglas generales.
Debería usar >=
para el umbral de 150 —
el kiwi con exactamente 150 ventas
debería recibir "alto rendimiento".
Con > recibiría
"rendimiento medio" — error silencioso
en el caso límite.
El resultado esperado:
manzana → alto, pera → medio,
naranja → agotado, kiwi → alto,
fresa → agotado.
La IA probablemente usará una lista de diccionarios — es la estructura más natural para representar registros con campos nombrados. También puede usar una lista de tuplas o directamente un DataFrame de pandas. Las tres opciones son válidas — la más legible para este caso es la lista de diccionarios o el DataFrame.
— Tarea 2: Validador de registros —
not nombre y
nombre == ""
no son completamente equivalentes:
not nombre también
detecta None
como nombre vacío, mientras que
nombre == ""
solo detecta el string vacío.
En datos reales donde el nombre
puede ser None,
not nombre es más robusto.
La condición "nota fuera del rango 0-10"
se expresa como
nota < 0 or nota > 10.
La forma con comparación encadenada
sería la negación:
not (0 <= nota <= 10).
La IA probablemente usará
or — es la más directa.
Para mostrar solo el primer error,
debe usar if/elif/else
— que se detiene en el primer
True.
Con múltiples if
independientes, un registro
con nombre vacío y nota incorrecta
mostraría los dos errores.
El resultado esperado:
Ana → aprobado,
registro vacío → error nombre,
Bob → error nota,
Carlos → inactivo,
Diana → suspenso.
— Tarea 3: Clasificador de archivos —
Ambas opciones son válidas.
match/case con
agrupación de extensiones es
más limpio para este caso
porque compara un valor fijo
contra múltiples opciones concretas.
if/elif con in
— if ext in {"csv", "tsv"}
— también es correcto y quizás
más familiar para la IA.
Las dos soluciones son aceptables.
Usará nombre.split(".")[-1]
para extraer la extensión —
el último elemento al separar
por punto. O el método
os.path.splitext(nombre)[1]
que es más robusto para nombres
con varios puntos.
Para normalizar a minúsculas:
.lower() antes
de comparar.
Aplicando .lower()
a la extensión antes de comparar,
"CSV" se convierte
en "csv" y
"JPG" en
"jpg" — ambos
clasificados correctamente.
"parquet" no está
en ninguna categoría →
"desconocido".
— Tarea 4: Análisis de DataFrame —
Para tres categorías la IA
probablemente usará
np.where() anidado:
df["rendimiento"] = np.where(
df["ventas"] >= 150, "alto",
np.where(df["ventas"] >= 80, "medio", "bajo")
)
O puede usar
pd.cut()
que es más elegante para rangos.
Ambas son correctas.
Para "precio_ajustado"
con dos casos usará
np.where() simple.
El filtro combinado debe usar
& con paréntesis:
df_filtrado = df[
(df["activo"]) &
(df["categoria"] == "fruta") &
(df["ventas"] > 0)
]
Con los datos de ejemplo pasan el filtro: manzana, pera y fresa. Naranja es cítrico, kiwi está inactivo y tiene ventas cero.
&
y no and,
y que cada condición va
entre paréntesis.
Sin paréntesis la precedencia
de operadores puede producir
resultados incorrectos.