Contenido del curso
Fundamentos de Python
Los fundamentos de Python incluyen la sintaxis (sangría para bloques de código), los tipos de datos básicos (numéricos, booleanos, cadenas de texto), las variables, el control de flujo (condicionales como if/elif/else y bucles como for/while), y las funciones (bloques de código reutilizables).
0/6
Operadores y Control de Flujo
Los operadores en Python se clasifican en varios tipos (aritméticos, de comparación, lógicos, de asignación, etc.), mientras que el control de flujo se refiere al orden en que se ejecutan las instrucciones, modificándolo con estructuras como if, elif, else (condicionales), y for o while (bucles). Las instrucciones break, continue y pass también controlan el flujo dentro de los bucles.
0/3
Funciones y Manejo de Errores
Las funciones en Python son bloques de código reutilizables, mientras que el manejo de errores (excepciones) se hace con los bloques try, except, else y finally para gestionar errores de ejecución y evitar que el programa se detenga abruptamente. try ejecuta un código, except lo captura si ocurre un error específico, else se ejecuta si no hay error y finally se ejecuta siempre, haya o no error.
0/3
Estructuras de Datos
Las estructuras de datos principales en Python son las listas, tuplas, diccionarios y conjuntos. Estos tipos de datos se diferencian por su mutabilidad (si sus elementos se pueden cambiar después de su creación) y si mantienen el orden de los elementos. Las listas son ordenadas y mutables, mientras que las tuplas son ordenadas e inmutables. Los diccionarios son colecciones no ordenadas de pares clave-valor, y los conjuntos son colecciones desordenadas de elementos únicos.
0/5
Programación Orientada a Objetos (POO)
La Programación Orientada a Objetos (POO) en Python es un paradigma que organiza el código en torno a objetos, que son instancias de clases. Las clases actúan como plantillas que definen los atributos (datos) y métodos (comportamientos) de los objetos, permitiendo crear programas más modularizados, reutilizables y fáciles de mantener. Python soporta conceptos clave de la POO como la herencia, el encapsulamiento y el polimorfismo.
0/5
Ambientes virtuales
Un entorno virtual de Python es un espacio aislado que permite instalar paquetes y dependencias específicos para un proyecto concreto sin afectar a otras aplicaciones o a la instalación global de Python. Se crea una carpeta con una instalación de Python y una copia local de pip dentro de este entorno, lo que permite a cada proyecto tener sus propias bibliotecas y versiones, evitando así conflictos entre diferentes proyectos que puedan requerir versiones distintas de la misma librería.
0/1
Archivos
El manejo de archivos en Python se realiza principalmente usando la función open() para abrir un archivo y los métodos read(), write(), append() y close() para manipularlo. Es crucial gestionar los archivos adecuadamente, cerrándolos para liberar recursos, aunque es más recomendable usar la sentencia with, que cierra el archivo automáticamente. Python permite trabajar con archivos de texto y binarios, así como con distintos modos de apertura como 'r' (solo lectura), 'w' (escritura/sobreescritura), y 'a' (añadir).
0/1
Módulos y Librerías Estándar
Un "módulo" en Python se refiere a dos conceptos distintos: un archivo .py con código que se puede importar para reutilizar funciones, clases y variables, y el operador % que calcula el residuo de una división entera. Ambos son útiles para organizar el código y resolver problemas matemáticos, respectivamente.
0/2
Hilos y tareas en Python
En Python, los hilos (threads) son secuencias de ejecución dentro de un proceso que permiten la concurrencia, ejecutando tareas simultáneamente para aprovechar mejor los recursos del sistema. Las tareas son las unidades de trabajo a realizar, como descargar archivos o procesar datos. Se utilizan para manejar operaciones que implican espera (I/O-bound) de forma eficiente, permitiendo que una aplicación no se bloquee mientras espera. Para ello, se usa el módulo threading, se crean objetos Thread que representan las tareas, se inician con .start() y se pueden sincronizar con mecanismos como Lock para evitar conflictos.
0/1
Curso de Programación en Pythón 3.

    🧭 Parte 1 — Introducción a las estructuras condicionales e instrucciones if, elif, else

    Las estructuras condicionales permiten que tu programa tome decisiones: si ocurre A, haz X; si no, haz Y. En Python, la toma de decisiones se implementa con if, elif y else, apoyadas por comparaciones y operadores lógicos.


    ✅ Sintaxis básica

    La regla de oro de Python es la indentación (sangría): define los bloques de código. Por convención, usa 4 espacios.

    
    edad = 20
    
    if edad >= 18:
        print("Eres mayor de edad")
    elif edad > 0:
        print("Eres menor de edad")
    else:
        print("Edad no válida")
      
    • if: evalúa una condición. Si es True, ejecuta su bloque.
    • elif: condiciones adicionales si las anteriores fueron False.
    • else: caso por defecto cuando todo lo anterior fue False.
    Tip: usa nombres de variables expresivos (tiene_licencia, saldo_disponible) para mejorar legibilidad.

    🧪 Operadores de comparación (devuelven True o False)

    OperadorSignificadoEjemploResultado
    ==Igual a5 == 5True
    !=Distinto de5 != 3True
    >, <Mayor / Menor7 < 2False
    >=, <=Mayor/menor o igual8 >= 8True
    inContiene"Py" in "Python"True
    isIdentidad (mismo objeto)x is NoneConvención c/ None
    
    usuario = "javier"
    if "jav" in usuario and len(usuario) >= 5:
        print("Usuario válido")
      

    🔗 Operadores lógicos: and, or, not

    Sirven para combinar condiciones.

    
    edad = 19
    tiene_licencia = True
    
    if edad >= 18 and tiene_licencia:
        print("Puedes conducir")
    elif edad >= 18 and not tiene_licencia:
        print("Necesitas licencia")
    else:
        print("Aún no puedes")
      
    OperadorReglaEjemploEvalúa
    andTrue si ambos son TrueTrue and FalseFalse
    orTrue si alguno es TrueFalse or TrueTrue
    notInvierte el valornot TrueFalse

    ⚡ Cortocircuito (short-circuit)

    Python deja de evaluar cuando ya conoce el resultado. Esto evita errores y mejora rendimiento.

    
    x = 0
    # 'and' se corta si la izquierda es False ⇒ no divide por cero
    seguro = (x != 0) and (10 / x > 2)
    
    token = "ABC123"
    # 'or' se corta si la izquierda es True ⇒ no llama a función costosa
    ok = (token is not None) or validar_token_costoso(token)
      

    🧠 Truthiness (valores “verdaderos” y “falsos”)

    En un if, Python considera falsos: False, None, 0, 0.0, "", [], (), {}, set(). Todo lo demás es verdadero.

    
    nombre = ""
    if nombre:
        print("Hola", nombre)
    else:
        print("Nombre no proporcionado")
      

    🧩 Patrones comunes con if, elif, else

    1) Validación de entrada

    
    texto = input("Edad: ")
    
    if texto.isdigit():
        edad = int(texto)
        if edad >= 18:
            print("Mayor de edad")
        else:
            print("Menor de edad")
    else:
        print("Entrada no válida")
      

    2) Rango con comparaciones encadenadas

    
    nota = 7.5
    if 0 <= nota <= 10:
        print("Nota válida")
    else:
        print("Nota fuera de rango")
      

    3) Múltiples ramas bien estructuradas

    
    rol = "editor"
    
    if rol == "admin":
        acceso = "total"
    elif rol == "editor":
        acceso = "parcial"
    elif rol == "invitado":
        acceso = "limitado"
    else:
        acceso = "denegado"
    
    print("Acceso:", acceso)
      

    🛡️ Errores típicos y cómo evitarlos

    • Olvidar el casting al leer input() (que siempre es str).
    • Usar == None en lugar de is None para comprobar ausencia de valor.
    • Comparar tipos incompatibles (p. ej., "10" > 5 lanza TypeError).
    • Indentación inconsistente (mezclar espacios y tabuladores). Configura tu editor a 4 espacios.
    
    # ✅ None seguro
    resultado = obtener_dato()
    if resultado is None:
        print("Sin datos")
      

    📏 Estilo y legibilidad desde el día 1

    • Prefiere condiciones positivas y simples: if activo: mejor que if not desactivado:
    • Evita “piramidarif: extrae funciones o usa retornos tempranos.
    • Usa variables booleanas intermedias para documentar la intención:
    
    es_mayor_de_edad = edad >= 18
    if es_mayor_de_edad:
        habilitar_compra()
      

    🧪 Mini-ejercicios

    1. Pide un número y muestra si es positivo, negativo o cero.
    2. Solicita usuario y contraseña: si ambos no están vacíos, imprime “Acceso concedido”, si no, “Acceso denegado”.
    3. Lee una nota (0–10) y muestra: “Matrícula (≥9)”, “Aprobado (≥5)”, “Suspenso (<5)”, o “Inválida”.

    🏁 Conclusión

    Ya dominas la base de las decisiones con if, elif y else. En la Parte 2 profundizaremos en condiciones anidadas y cómo simplificarlas sin perder legibilidad.

    🧱 Parte 2 — Condiciones anidadas

    Las condiciones anidadas aparecen cuando colocas uno o más if dentro de otro if. Son útiles, pero abusar de ellas crea la famosa pirámide del terror: código difícil de leer, probar y mantener. En esta parte aprenderás a anidar bien… y a evitar anidar cuando conviene.


    🔎 Ejemplo básico de anidación

    
    usuario = {"activo": True, "rol": "editor", "edad": 21}
    
    if usuario:
        if usuario["activo"]:
            if usuario["edad"] >= 18:
                if usuario["rol"] in ("admin", "editor"):
                    print("Acceso concedido")
                else:
                    print("Rol insuficiente")
            else:
                print("Menor de edad")
        else:
            print("Usuario inactivo")
    else:
        print("Usuario inexistente")
      

    Funciona… pero es poco legible. Veamos técnicas para simplificar.


    🛡️ Patrón 1 — Guard Clauses (retornos tempranos)

    Consiste en salir antes cuando falla un requisito. Dejas el camino feliz más plano.

    
    def puede_acceder(usuario):
        if not usuario:
            return "Usuario inexistente"
        if not usuario.get("activo"):
            return "Usuario inactivo"
        if usuario.get("edad", 0) < 18:
            return "Menor de edad"
        if usuario.get("rol") not in ("admin", "editor"):
            return "Rol insuficiente"
        return "Acceso concedido"
    
    print(puede_acceder({"activo": True, "rol": "editor", "edad": 21}))
      
    • Ventaja: menos indentación, intención más clara.
    • Tip: ordena las comprobaciones de la más barata a la más cara.

    🧰 Patrón 2 — Combinación de condiciones

    Si dependes de varias condiciones booleanas, combínalas con and/or o usa variables intermedias con nombres claros.

    
    edad_ok = usuario and usuario.get("edad", 0) >= 18
    rol_ok = usuario and usuario.get("rol") in ("admin", "editor")
    activo_ok = usuario and usuario.get("activo") is True
    
    if usuario and activo_ok and edad_ok and rol_ok:
        print("Acceso concedido")
    else:
        print("Acceso denegado")
      

    Mejor aún con explicadores (variables booleanas con intención).


    🧠 Patrón 3 — De Morgan para simplificar negaciones

    Evita acumular not anidados reescribiendo la lógica:

    Antes (difícil)Después (claro)
    if not (a and b):if not a or not b:
    if not (x or y):if not x and not y:
    
    # Evita doble negación:
    if not (activo_ok and edad_ok):
        print("Falta requisito")
      

    🧩 Patrón 4 — Salidas tempranas con excepciones (en validaciones estrictas)

    En funciones de validación, es más claro fallar rápido lanzando excepciones.

    
    def validar_pedido(pedido):
        if not pedido:
            raise ValueError("Pedido vacío")
        if pedido["importe"] <= 0:
            raise ValueError("Importe inválido")
        if pedido["estado"] not in {"nuevo", "pagado"}:
            raise ValueError("Estado no permitido")
        return True
      

    Queda código lineal y sin nidos. Maneja las excepciones donde corresponda.


    🧮 Patrón 5 — Diccionarios como “switch” (mapa de decisiones)

    Para decisiones por valor (antes de match), usa un diccionario de funciones o resultados.

    
    def crear(): return "Creando…"
    def leer():  return "Leyendo…"
    def borrar(): return "Borrando…"
    
    acciones = {
        "CREATE": crear,
        "READ": leer,
        "DELETE": borrar,
    }
    
    oper = "READ"
    resultado = acciones.get(oper, lambda: "Operación no soportada")()
    print(resultado)
      

    Menos elif, más claridad y extensibilidad.


    📏 Patrón 6 — Validaciones encadenadas (pipeline)

    Encapsula reglas en funciones pequeñas y compón un pipeline.

    
    def regla_edad(u):   return (u.get("edad", 0) >= 18, "Menor de edad")
    def regla_activo(u): return (u.get("activo") is True, "Usuario inactivo")
    def regla_rol(u):    return (u.get("rol") in {"admin","editor"}, "Rol insuficiente")
    
    def validar(u):
        for check, msg in (regla_edad, regla_activo, regla_rol):
            ok, error = check(u)
            if not ok:
                return error
        return "Acceso concedido"
    
    print(validar({"edad": 21, "activo": True, "rol": "editor"}))
      

    🧪 Antes y después — Refactor real

    ❌ Versión anidada

    
    def precio_final(p, cupon):
        if p > 0:
            if cupon is not None:
                if cupon == "DESC10":
                    return p * 0.9
                else:
                    return p
            else:
                return p
        else:
            return 0
      

    ✅ Versión plana (guard clauses + early return)

    
    def precio_final(p, cupon):
        if p <= 0:
            return 0
        if cupon == "DESC10":
            return p * 0.9
        return p
      

    Menos ramas, misma lógica, mayor legibilidad.


    ⚡ Cortocircuito para proteger operaciones

    
    # Evita división por cero
    den = 0
    seguro = (den != 0) and (100 / den > 1)  # No evalúa la derecha si den == 0
      

    Úsalo siempre que una comprobación pueda evitar errores (E/S, llamadas costosas, etc.).


    🧼 Checklist anti-pirámide

    • ¿Puedo salir antes con un retorno/continue/break?
    • ¿Puedo combinar condiciones o extraer booleanos con nombre?
    • ¿Este elif es en realidad un diccionario de decisiones?
    • ¿Puedo mover validaciones a funciones pequeñas (pipeline)?
    • ¿Hay dobles negaciones que simplificar con De Morgan?

    🧠 Mini-ejercicios

    1. Guard clauses: refactoriza una función de login con 4 niveles de if para que tenga retornos tempranos (usuario, activo, password, MFA).
    2. Switch dict: implementa un router de comandos (ADD, LIST, DELETE) sin usar múltiples elif.
    3. Pipeline: escribe tres reglas de validación sobre un formulario y compón un validador que devuelva el primer error.

    🏁 Conclusión

    Has aprendido a controlar la complejidad de las condiciones anidadas con patrones profesionales: guard clauses, diccionarios como switch, pipelines y reglas de estilo. En la Ahora veremos el operador ternario para expresiones concisas y seguras.

    ⚡ Parte 3 — Operador ternario en Python

    El llamado operador ternario en Python se expresa como una expresión condicional con la forma:
    valor_si_verdadero if condicion else valor_si_falso. Su objetivo es escribir condiciones simples en una sola línea sin perder legibilidad.


    ✅ Sintaxis básica

    
    edad = 19
    mensaje = "Mayor de edad" if edad >= 18 else "Menor de edad"
    print(mensaje)
      
    • Se usa cuando hay dos ramas simples y ambas devuelven un valor.
    • Es una expresión: puede aparecer en asignaciones, f-strings, retornos, comprensiones, etc.

    🧩 Comparación con if/else de bloque

    Bloque tradicionalTernarioCuándo usar
    
    if puntos >= 60:
        estado = "aprobado"
    else:
        estado = "suspendido"
    
    
    estado = "aprobado" if puntos >= 60 else "suspendido"
    
    Cuando la lógica es corta y devuelve un valor directo.

    🎯 Usos frecuentes

    1) Asignación condicional

    
    descuento = 0.10 if cliente_vip else 0.00
      

    2) En return dentro de funciones

    
    def etiqueta_edad(edad):
        return "18+" if edad >= 18 else "U18"
      

    3) En f-strings

    
    stock = 3
    print(f"Estado: {'OK' if stock > 0 else 'SIN STOCK'}")
      

    4) En comprensiones

    
    nums = [1, 2, 3, 4, 5]
    paridad = ["par" if n % 2 == 0 else "impar" for n in nums]
    print(paridad)  # ['impar', 'par', 'impar', 'par', 'impar']
      

    🧠 Ternario vs. patrones con and/or

    En Python existe el idiom cond and a or b, pero no es equivalente en todos los casos por la truthiness (si a es falsy, se devolverá b aunque la condición sea verdadera). Prefiere SIEMPRE el ternario estándar.

    
    # ❌ Evita:
    resultado = condicion and valor_si_verdadero or valor_si_falso
    
    # ✅ Correcto:
    resultado = valor_si_verdadero if condicion else valor_si_falso
      

    🧼 Buenas prácticas de legibilidad

    • Úsalo para decisiones simples (una sola condición, dos resultados claros).
    • Evita anidar ternarios si pierdes claridad. Prefiere if/elif/else de bloque o match (Parte 4).
    • Coloca la condición en el centro y mantén valores cortos a izquierda/derecha.
    • No metas efectos secundarios (llamadas complejas) dentro del ternario; evalúa antes en variables.
    
    # 👎 Difícil de leer:
    msg = "OK" if x > 0 else ("CERO" if x == 0 else "NEGATIVO")
    
    # 👍 Mejor:
    if x > 0:
        msg = "OK"
    elif x == 0:
        msg = "CERO"
    else:
        msg = "NEGATIVO"
      

    🛡️ Patrones seguros

    1) Evitar llamadas costosas duplicadas

    
    precio = obtener_precio()
    precio_final = (precio * 0.9) if es_vip else precio
      

    2) Normalizar valores con None

    
    nombre = None
    visible = nombre if nombre is not None else "Anónimo"
      

    3) Mensajes / etiquetas cortas

    
    valido = len(password) >= 8
    estado = "✔️ Fuerte" if valido else "⚠️ Débil"
      

    4) Casting condicionado

    
    dato = "3.14"
    num = float(dato) if dato.replace('.', '', 1).isdigit() else None
      

    🔍 Anti-patrones y errores comunes

    • Demasiadas condiciones en un ternario → usa if/elif/else o match.
    • Usar el idiom cond and A or B → puede fallar si A es falsy.
    • Romper líneas sin paréntesis → usa paréntesis para mejorar lectura.
    
    # 👎 Ternario largo sin paréntesis
    resultado = "A" if a and b or (c and not d) else "B"
    
    # 👍 Más claro
    resultado = (
        "A"
        if (a and b) or (c and not d)
        else "B"
    )
      

    🧪 Mini-ejercicios

    1. Crear una variable riesgo que sea "ALTO" si temp >= 38.0 y "OK" en caso contrario.
    2. Con un valor saldo, definir estado como "en descubierto" si saldo < 0 y "positivo" si no.
    3. Dada una cadena correo, asignar valido a True si contiene "@" y "." (ambas), si no False (puedes usar un ternario o una expresión booleana directa).
    4. Generar una lista signos desde nums con "+" si el número es >= 0 y "-" si es < 0 (usa comprensión con ternario).

    🏁 Conclusión

    El ternario de Python es ideal para decisiones concisas que devuelven un valor. Úsalo con mesura y premia la claridad. En la Ahora veremos cómo cubrir casos múltiples con switch/match, la alternativa moderna y legible para varias ramas.

    🔀 Parte 4 — Estructura switch y match-case en Python

    Python no tuvo switch tradicional como C/Java. Históricamente se resolvía con diccionarios de despacho (dispatch dict) o cadenas de if/elif. Desde Python 3.10, disponemos de match (coincidencia estructural) para casos múltiples, potente y legible.

    Compatibilidad: match requiere Python ≥ 3.10. Si apuntas a versiones anteriores, usa el patrón de diccionario de funciones.

    🧰 Patrón “switch” con diccionario de despacho

    Mapa de clave → acción. Evita múltiples elif, es más limpio y escalable.

    
    def crear():  return "Creando…"
    def leer():   return "Leyendo…"
    def borrar(): return "Borrando…"
    
    DISPATCH = {
        "CREATE": crear,
        "READ":   leer,
        "DELETE": borrar,
    }
    
    oper = "READ"
    resultado = DISPATCH.get(oper, lambda: "Operación no soportada")()
    print(resultado)  # "Leyendo…"
      
    • Ventajas: extensible, testable, muy claro para comandos.
    • Fallback: provee una función por defecto via .get(..., lambda: ...).

    🧠 Variantes del dispatch

    1) Con argumentos

    
    def cuadrado(x): return x*x
    def doble(x):    return 2*x
    
    ops = {"sq": cuadrado, "dbl": doble}
    cmd, valor = "dbl", 7
    print(ops.get(cmd, lambda v: v)(valor))  # 14
      

    2) Con valores de retorno simples

    
    mensajes = {
        200: "OK",
        404: "No encontrado",
        500: "Error interno",
    }
    codigo = 404
    print(mensajes.get(codigo, "Desconocido"))
      

    3) Con clases/estrategias

    
    class Crear:  def __call__(self): return "Creando…"
    class Leer:   def __call__(self): return "Leyendo…"
    class Borrar: def __call__(self): return "Borrando…"
    
    acciones = {"CREATE": Crear(), "READ": Leer(), "DELETE": Borrar()}
    print(acciones.get("DELETE", lambda: "No soportado")())
      

    🧩 match-case: coincidencia estructural (Python ≥ 3.10)

    match permite describir formas de los datos (patrones). Es más que un switch: entiende literales, tuplas, secuencias, dict, clases, uniones, y añade guardas (if en el case).

    ✅ Literales y default (_)

    
    def estado_http(codigo):
        match codigo:
            case 200:
                return "OK"
            case 404:
                return "No encontrado"
            case 500:
                return "Error interno"
            case _:
                return "Desconocido"
    
    print(estado_http(404))
      

    🎯 Varios literales en un mismo caso (OR)

    
    def es_vocal(c):
        match c.lower():
            case "a" | "e" | "i" | "o" | "u":
                return True
            case _:
                return False
      

    📦 Patrones de secuencia (listas/tuplas)

    
    def forma(secuencia):
        match secuencia:
            case [x, y]:               # lista de 2 elementos
                return f"Par: ({x}, {y})"
            case [x, y, z]:            # lista de 3
                return f"Triple: {x},{y},{z}"
            case [primero, *_]:        # lista con al menos 1 (resto ignorado)
                return f"Empieza por {primero}"
            case _:
                return "No coincide"
    
    print(forma([10, 20, 30]))
      

    🗺️ Patrones de diccionario

    
    def describe(evento):
        match evento:
            case {"type": "login", "user": usuario}:
                return f"Login de {usuario}"
            case {"type": "error", "code": c} if c >= 500:   # guarda
                return f"Error grave {c}"
            case {"type": "error", "code": c}:
                return f"Error {c}"
            case _:
                return "Evento desconocido"
    
    print(describe({"type": "error", "code": 503}))
      

    🏷️ Patrones de clase (con atributos)

    
    from dataclasses import dataclass
    
    @dataclass
    class Pedido:
        id: int
        importe: float
        vip: bool = False
    
    def procesa(p):
        match p:
            case Pedido(id=pid, importe=imp, vip=True) if imp >= 100:
                return f"Pedido VIP {pid} con descuento"
            case Pedido(id=pid, importe=imp):
                return f"Pedido {pid} por {imp:.2f}"
            case _:
                return "Objeto no compatible"
    
    print(procesa(Pedido(101, 120, True)))
      

    🧪 Patrones con alias (as)

    
    def analizar(x):
        match x:
            case {"ok": True, **resto} as completo:
                # alias 'completo' captura el dict entero
                return ("válido", completo)
            case _:
                return ("inválido", x)
    
    print(analizar({"ok": True, "data": [1,2,3]}))
      

    🛡️ Guardas (condiciones en el case)

    Permiten refinar el patrón con una condición booleana.

    
    def clasificar(n):
        match n:
            case int() if n < 0:
                return "negativo"
            case int() if n == 0:
                return "cero"
            case int():
                return "positivo"
            case float():
                return "decimal"
            case _:
                return "no numérico"
      

    ⚠️ Errores comunes y cómo evitarlos

    • Olvidar el default: usa case _: para cubrir casos no contemplados.
    • Patrones demasiado específicos: si el dato cambia levemente, no coincidirá. Añade guardas o casos más amplios.
    • Versión de Python: si distribuyes a entornos < 3.10, provee fallback (dispatch dict).
    • Lógica pesada en casos: mueve el trabajo a funciones; los case deben ser declarativos.

    🧼 Buenas prácticas

    • Para comandos/acciones sencillas → usa dispatch dict.
    • Para estructuras de datos (secuencias, dicts, objetos) → usa match.
    • Usa guardas para reglas de negocio sin sobre-crear patrones.
    • Mantén cada case corto; delega a funciones y retorna temprano.

    🧪 Mini-ejercicios

    1. Implementa un router con dispatch dict para POST, GET, PUT, DELETE; añade fallback.
    2. Usa match para identificar tokens de una calculadora: ('+', a, b), ('num', x), ('err', msg).
    3. Con match, procesa eventos de pago: {"type":"paid","amount":>=100} aplica descuento VIP (usa guarda), otros muestran total normal.

    🏁 Conclusión

    Python ofrece dos vías para “switch”: el dispatch dict (simple, retrocompatible) y match-case (poderoso y expresivo). Elige según tu audiencia (versión de Python) y la complejidad de los datos.

    🧼 Parte 5 — Buenas prácticas de legibilidad en estructuras condicionales

    En Python, escribir código que funcione es solo el primer paso; lo que diferencia a un buen desarrollador es su capacidad de escribir código que se entienda, se mantenga y se escale.
    Las estructuras condicionales (if, elif, else, match) son uno de los puntos donde más se nota la calidad del estilo.
    Aquí aprenderás a aplicar la filosofía del “Zen de Python” a tus decisiones lógicas.


    📜 Recordemos algunos principios del Zen de Python

    
    Beautiful is better than ugly.  
    Simple is better than complex.  
    Readability counts.  
    There should be one—and preferably only one—obvious way to do it.
        

    Estas líneas se aplican directamente a cómo escribes tus condiciones. Menos anidaciones, nombres claros y estructura coherente.


    ✅ 1. Mantén las condiciones simples y legibles

    Evita expresiones complejas con varios operadores lógicos encadenados. Divide en pasos o usa variables auxiliares descriptivas.

    
    # 👎 Difícil de leer
    if (edad >= 18 and permiso and not suspendido and (pais == "ES" or pais == "PT")):
        print("Puede conducir")
    
    # 👍 Mejor legibilidad
    mayor_edad = edad >= 18
    pais_valido = pais in ("ES", "PT")
    if mayor_edad and permiso and not suspendido and pais_valido:
        print("Puede conducir")
      

    🧱 2. Usa guard clauses para evitar pirámides

    En lugar de anidar condicionales, sal temprano del flujo cuando una comprobación falla. Esto aplanará tu estructura.

    
    def procesar(usuario):
        if not usuario:
            return "Error: usuario inexistente"
        if not usuario.get("activo"):
            return "Error: usuario inactivo"
        if usuario.get("edad", 0) < 18:
            return "Error: menor de edad"
        return "Usuario válido"
      

    Ventaja: mejora la lectura y reduce el número de tabulaciones.


    🔍 3. Evalúa expresiones en el orden más lógico

    Ordena las comprobaciones de la más rápida y segura a la más compleja o costosa.
    Aprovecha el cortocircuito (and/or) para evitar errores y optimizar rendimiento.

    
    # 👎 Mal orden
    if usuario["activo"] and usuario and usuario["edad"] > 18:
        ...
    
    # 👍 Correcto
    if usuario and usuario.get("activo") and usuario.get("edad", 0) > 18:
        ...
      

    🧠 4. Prefiere condiciones positivas

    Las condiciones positivas son más naturales de leer que las negativas.
    En vez de “si no está inactivo”, escribe “si está activo”.

    
    # 👎 Negativo doble
    if not no_valido:
        procesar()
    
    # 👍 Positivo directo
    if valido:
        procesar()
      

    🧩 5. Evita redundancias en if

    
    # 👎 Redundante
    if es_valido == True:
        ejecutar()
    
    # 👍 Correcto
    if es_valido:
        ejecutar()
      

    Del mismo modo, no compares booleanos con == False; usa not variable.


    ⚙️ 6. Agrupa condiciones similares

    Si varias condiciones producen el mismo resultado, agrúpalas con in o tuplas.

    
    # 👎
    if pais == "ES" or pais == "PT" or pais == "FR":
        print("País admitido")
    
    # 👍
    if pais in ("ES", "PT", "FR"):
        print("País admitido")
      

    🧮 7. Usa funciones pequeñas para separar decisiones

    Una función que tenga muchos if puede dividirse en funciones auxiliares con un nombre descriptivo.
    Esto mejora el entendimiento y favorece el testeo.

    
    def es_mayor(edad): return edad >= 18
    def tiene_permiso(u): return u.get("permiso", False)
    def puede_conducir(u):
        return es_mayor(u.get("edad", 0)) and tiene_permiso(u)
      

    🧾 8. Sigue las recomendaciones PEP 8

    ReglaEjemplo correctoIncorrecto
    Espacios antes y después de operadoresif x == 10:if x==10:
    Indentación de 4 espacios
    
    if cond:
        hacer_algo()
            
    
    if cond:
      hacer_algo()
            
    Sin paréntesis innecesariosif activo:if (activo):
    Usar is/is not con Noneif valor is None:if valor == None:

    💬 9. Escribe comentarios útiles, no obvios

    
    # 👎 Innecesario
    if x > 0:  # Si x es mayor que cero
        ...
    
    # 👍 Explica la intención, no el código
    if x > 0:  # Evitamos divisiones por cero en la siguiente operación
        ...
      

    📦 10. Usa match cuando tengas muchos casos

    En lugar de cadenas de elif, match mejora la lectura y te fuerza a cubrir todos los casos.
    Es ideal cuando tienes niveles, tipos, estados o comandos.

    
    def clasificar(puntaje):
        match puntaje:
            case p if p >= 90: return "Excelente"
            case p if p >= 70: return "Aprobado"
            case p if p >= 50: return "Regular"
            case _: return "Insuficiente"
      

    El orden lógico de evaluación mejora la comprensión y reduce errores.


    🧪 Ejemplo completo — Antes y después

    ❌ Código sin estilo

    
    def procesar(p):
        if p:
            if p["monto"] > 0:
                if p["estado"] == "pagado":
                    print("Pedido confirmado")
                else:
                    print("Estado no válido")
            else:
                print("Monto incorrecto")
        else:
            print("Sin datos")
      

    ✅ Código limpio

    
    def procesar(p):
        if not p:
            return "Sin datos"
        if p["monto"] <= 0:
            return "Monto incorrecto"
        if p["estado"] != "pagado":
            return "Estado no válido"
        return "Pedido confirmado"
      

    Resultado: menos indentación, más claridad, control de flujo evidente.


    🧠 Mini-ejercicios

    1. Refactoriza una función con 4 niveles de if usando guard clauses.
    2. Usa variables intermedias descriptivas para reemplazar una condición de más de 3 operadores.
    3. Convierte una serie de elif en un match con casos claros y un _ por defecto.
    4. Revisa un código tuyo e identifica comparaciones redundantes (por ejemplo, if var == True).

    🏁 Conclusión

    La legibilidad es la piedra angular del código profesional. En Python, las estructuras condicionales deben reflejar la intención, no solo la lógica.
    Recuerda: menos anidaciones, más claridad; nombres expresivos, menos comentarios obvios.

    En la Parte 6 aplicaremos todo lo aprendido con ejercicios integradores que combinan if, elif, else, match y las buenas prácticas vistas hasta ahora.

    🎯 Parte 6 — Ejercicios prácticos y resumen final

    Has llegado al final del módulo de Estructuras Condicionales.
    Ahora consolidaremos lo aprendido con ejercicios prácticos que mezclan if, elif, else, el operador ternario y la estructura moderna match-case.
    Aquí pondrás en práctica tus habilidades para analizar, decidir y escribir código claro.


    🧩 Ejercicio 1 — Clasificador de temperatura

    Escribe un programa que pida la temperatura actual y clasifique el clima.

    
    temp = float(input("Temperatura actual (°C): "))
    
    if temp <= 0:
        print("❄️ Muy frío")
    elif temp <= 15:
        print("🌤 Frío")
    elif temp <= 25:
        print("☀️ Templado")
    elif temp <= 35:
        print("🔥 Caluroso")
    else:
        print("🥵 Extremo")
      

    Conceptos: comparaciones encadenadas, elif múltiple, legibilidad y claridad.


    ⚙️ Ejercicio 2 — Menú interactivo con match-case

    Implementa un pequeño menú de opciones usando match (Python ≥ 3.10).

    
    print("=== MENÚ PRINCIPAL ===")
    print("1. Saludar")
    print("2. Mostrar hora")
    print("3. Salir")
    
    opcion = input("Elige una opción (1-3): ")
    
    match opcion:
        case "1":
            print("👋 ¡Hola, usuario!")
        case "2":
            from datetime import datetime
            print("🕒 Hora actual:", datetime.now().strftime("%H:%M:%S"))
        case "3":
            print("👋 Hasta pronto.")
        case _:
            print("❌ Opción no válida")
      

    Conceptos: match-case, patrón por literales, _ (default), modularidad.


    📦 Ejercicio 3 — Validación de formulario

    Usa condiciones y retornos tempranos para validar datos de usuario.

    
    def validar_formulario(nombre, edad, correo):
        if not nombre:
            return "❌ Error: el nombre es obligatorio."
        if not edad.isdigit() or int(edad) <= 0:
            return "❌ Error: la edad debe ser un número positivo."
        if "@" not in correo or "." not in correo:
            return "❌ Error: el correo no es válido."
        return "✅ Formulario válido."
    
    # Ejemplo de prueba
    print(validar_formulario("Javier", "25", "javi@example.com"))
      

    Conceptos: guard clauses, validación booleana, composición lógica.


    🧮 Ejercicio 4 — Calculadora avanzada (ternario + match)

    
    print("=== CALCULADORA ===")
    a = float(input("Número 1: "))
    b = float(input("Número 2: "))
    op = input("Operación (+, -, *, /): ")
    
    match op:
        case "+":
            print(f"Resultado: {a + b}")
        case "-":
            print(f"Resultado: {a - b}")
        case "*":
            print(f"Resultado: {a * b}")
        case "/":
            print("Resultado:", a / b if b != 0 else "❌ División por cero")
        case _:
            print("Operación no válida")
      

    Conceptos: match, ternario, condiciones anidadas simplificadas.


    🧠 Ejercicio 5 — Clasificador de edades

    Combina el uso del operador ternario dentro de una lista por comprensión.

    
    edades = [3, 12, 17, 25, 65]
    categorias = [
        "Niño" if e < 13 else "Adolescente" if e < 18 else "Adulto" if e < 60 else "Mayor"
        for e in edades
    ]
    print(categorias)
      

    Conceptos: ternario anidado, comprensión de listas, legibilidad.


    🧠 Ejercicio 6 — Sistema de acceso (anidación + refactorización)

    Versión “antes y después” para aplicar buenas prácticas.

    ❌ Versión inicial (anidada)

    
    usuario = {"nombre": "Javier", "activo": True, "rol": "admin"}
    
    if usuario:
        if usuario["activo"]:
            if usuario["rol"] == "admin":
                print("✅ Acceso total")
            else:
                print("⛔ Acceso restringido")
        else:
            print("🚫 Usuario inactivo")
    else:
        print("❌ Usuario no encontrado")
      

    ✅ Versión refactorizada (guard clauses)

    
    if not usuario:
        print("❌ Usuario no encontrado")
    elif not usuario["activo"]:
        print("🚫 Usuario inactivo")
    elif usuario["rol"] != "admin":
        print("⛔ Acceso restringido")
    else:
        print("✅ Acceso total")
      

    Conceptos: simplificación estructural, claridad, control de flujo limpio.


    🎨 Ejercicio 7 — Analizador de productos (match con diccionarios)

    
    productos = [
        {"nombre": "Manzana", "categoria": "Fruta", "precio": 1.2},
        {"nombre": "Leche", "categoria": "Lácteo", "precio": 2.0},
        {"nombre": "Pan", "categoria": "Cereal", "precio": 1.0},
    ]
    
    for p in productos:
        match p:
            case {"categoria": "Fruta", "precio": precio} if precio < 2:
                print(f"🍎 {p['nombre']} en promoción")
            case {"categoria": "Lácteo"}:
                print(f"🥛 {p['nombre']} requiere refrigeración")
            case {"categoria": "Cereal"}:
                print(f"🍞 {p['nombre']} de panadería")
            case _:
                print(f"{p['nombre']} no clasificado")
      

    Conceptos: match-case, patrones de diccionarios, guardas y estructura clara.


    💬 Mini-repaso visual

    EstructuraUso recomendadoEjemplo
    if / elif / elseDecisiones simples o jerárquicasif edad >= 18: ...
    Condiciones anidadasEvitar; usar guardas o funcionesif a: if b: ...
    Operador ternarioAsignaciones brevesmsg = "OK" if ok else "ERR"
    Dispatch dict“Switch” retrocompatibleacciones.get(cmd, defecto)()
    match-caseCasos múltiples con patrones estructuradosmatch x: case _: ...

    🧾 Autoevaluación final

    Verifica si ya dominas estas competencias:

    • ✅ Comprendo cuándo usar if, elif y else.
    • ✅ Sé escribir condiciones limpias y sin redundancias.
    • ✅ Puedo usar guard clauses para simplificar estructuras.
    • ✅ Sé aplicar el operador ternario con legibilidad.
    • ✅ Sé usar match-case para manejar múltiples ramas de decisión.

    🏁 Conclusión de la Lección

    Las estructuras condicionales son el corazón del control de flujo en Python.
    Dominar su sintaxis es solo el comienzo; el verdadero objetivo es escribir código claro, coherente y expresivo.
    Recuerda:

    • 🧠 Piensa antes de anidar. Cada nivel extra de indentación es una capa más de complejidad.
    • 💡 Refactoriza temprano. Convierte condiciones en funciones descriptivas.
    • ⚙️ Usa el operador ternario para expresiones simples.
    • 🧩 Explora match-case para decisiones más expresivas.
    • 📏 Lee tu código en voz alta: si suena complicado, probablemente lo sea.

    ➡️ En la próxima lección entraremos en el fascinante mundo de las estructuras de repetición (for, while do while), donde aprenderás a automatizar tareas y recorrer colecciones con elegancia.

    Nuestra puntuación
    ¡Haz clic para puntuar esta entrada!
    (Votos: 0 Promedio: 0)
    Scroll al inicio