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 Funciones en Python

    Las funciones son uno de los pilares de la programación en Python.
    Una función es un bloque de código reutilizable que realiza una tarea específica.
    Permite dividir un programa en partes más pequeñas y organizadas, facilitando la
    lectura, el mantenimiento y la reutilización del código.


    1.1 ¿Por qué usar funciones?

    • 🔹 Reutilización: evita repetir el mismo código varias veces.
    • 🔹 Legibilidad: hace que el código sea más fácil de entender.
    • 🔹 Organización: divide programas grandes en partes lógicas.
    • 🔹 Mantenimiento: facilita corregir errores o ampliar el programa.

    Piensa en una función como una herramienta dentro de tu caja de programación:
    puedes crearla una vez y usarla siempre que la necesites.


    1.2 Definición y sintaxis básica

    En Python, una función se define con la palabra reservada def, seguida del
    nombre de la función, paréntesis y dos puntos.
    El cuerpo de la función se escribe con sangría (normalmente cuatro espacios).

    # Sintaxis básica de una función
    def nombre_funcion():
        # bloque de código
        instruccion_1
        instruccion_2
        ...
        return valor

    Ejemplo práctico:

    def saludar():
        print("¡Hola! Bienvenido al curso de Python.")
    
    # Llamada a la función
    saludar()

    Salida esperada:

    ¡Hola! Bienvenido al curso de Python.
    💡 Recuerda: el bloque dentro de una función solo se ejecuta cuando la función es llamada.

    1.3 Nombres de funciones válidos

    Los nombres de funciones siguen las mismas reglas que las variables:

    • Deben comenzar con una letra o guion bajo (_).
    • Pueden contener letras, números y guiones bajos.
    • No pueden comenzar con un número.
    • No pueden coincidir con palabras reservadas del lenguaje (if, while, class, etc.).
    Válidos ✅Inválidos ❌
    saludar3funcion
    calcular_totaldef
    _imprimir_mensajesaludar!

    1.4 Bloque y retorno

    Una función puede o no devolver un valor.
    Si se utiliza la palabra return, la función “envía” ese resultado al lugar
    donde fue llamada. Si no hay return, la función devuelve None.

    def obtener_saludo():
        return "Hola desde la función."
    
    mensaje = obtener_saludo()
    print(mensaje)

    Salida:

    Hola desde la función.
    📘 Concepto clave: return detiene la ejecución de la función y devuelve el valor especificado.

    1.5 Ejemplo de estructura completa

    def calcular_area_rectangulo(base, altura):
        area = base * altura
        return area
    
    resultado = calcular_area_rectangulo(5, 3)
    print("El área es:", resultado)

    Salida esperada:

    El área es: 15

    1.6 Comentarios y documentación interna

    Es buena práctica agregar comentarios dentro de las funciones
    para explicar lo que hacen, especialmente si otros programadores
    (o tú mismo en el futuro) leerán ese código.

    def calcular_precio_total(precio, iva):
        """
        Calcula el precio total aplicando el IVA.
        Parámetros:
            precio: importe base del producto
            iva: porcentaje del IVA (por ejemplo, 21 para 21%)
        Retorna:
            Precio final con IVA incluido
        """
        total = precio * (1 + iva / 100)
        return total

    Luego puedes ver esta documentación con:

    help(calcular_precio_total)

    1.7 Buenas prácticas al definir funciones

    • ✅ Usa nombres descriptivos que indiquen la acción (calcular_total, obtener_promedio).
    • ✅ Mantén tus funciones cortas y con una sola responsabilidad.
    • ✅ Añade docstrings para documentarlas.
    • ✅ Evita modificar variables globales dentro de ellas (verás esto en el tema de scope).

    1.8 Mini-quiz

    1. ¿Qué palabra clave se usa para definir una función en Python?
    2. ¿Qué ocurre si una función no tiene return?
    3. ¿Qué diferencia hay entre print() y return?
    💡 Respuestas
    • 1️⃣ def
    • 2️⃣ Devuelve None
    • 3️⃣ print() muestra el resultado; return lo devuelve al programa.

    1.9 Prácticas guiadas

    Ejercicio 1 — Saludo personalizado

    def saludar(nombre):
        print(f"Hola, {nombre} 👋 Bienvenido al curso.")
    
    saludar("Javier")

    Ejercicio 2 — Suma de dos números

    def sumar(a, b):
        return a + b
    
    resultado = sumar(5, 7)
    print("Resultado:", resultado)

    Ejercicio 3 — Conversor de grados

    def celsius_a_fahrenheit(c):
        return (c * 9/5) + 32
    
    temp = celsius_a_fahrenheit(25)
    print("25°C =", temp, "°F")



    Parte 2: Parámetros y Argumentos en Python

    Las funciones son más útiles cuando pueden recibir información para trabajar con ella.
    Esa información se pasa a través de los parámetros y los argumentos.


    2.1 Parámetros vs Argumentos

    TérminoDescripciónEjemplo
    ParámetrosVariables que se definen en la cabecera de la función.def sumar(a, b):
    ArgumentosValores reales que se pasan al llamar a la función.sumar(5, 7)

    En resumen: los parámetros son los nombres; los argumentos, los valores.

    def saludar(nombre):
        print(f"Hola, {nombre}!")
    
    saludar("Javier")  # 'nombre' es el parámetro, "Javier" es el argumento

    2.2 Parámetros con valores por defecto

    Podemos asignar un valor predeterminado a un parámetro.
    Si no se pasa un argumento, se usará ese valor.

    def saludar(nombre="visitante"):
        print(f"Hola, {nombre} 👋")
    
    saludar()           # Usa el valor por defecto
    saludar("Javier")   # Sobrescribe el valor por defecto

    Salida:

    Hola, visitante 👋  
    Hola, Javier 👋
    💡 Los parámetros con valores por defecto deben ir al final de la lista de parámetros.
    # Correcto ✅
    def conectar(host, puerto=3306): ...
    
    # Incorrecto ❌ (dará error)
    def conectar(puerto=3306, host): ...

    2.3 Argumentos por posición y por nombre (keyword arguments)

    Los argumentos se pueden pasar de dos formas:

    • Por posición (orden de los parámetros).
    • Por nombre (keyword arguments).
    def mostrar_datos(nombre, edad, ciudad):
        print(f"Nombre: {nombre}, Edad: {edad}, Ciudad: {ciudad}")
    
    # Por posición
    mostrar_datos("Javier", 30, "Madrid")
    
    # Por nombre
    mostrar_datos(ciudad="Madrid", nombre="Javier", edad=30)

    Ambos métodos producen el mismo resultado, pero los argumentos por nombre son más claros y evitan errores si el orden cambia.


    2.4 Número variable de argumentos: *args

    A veces no sabes cuántos argumentos se pasarán a la función.
    Para eso se usa *args, que agrupa los argumentos posicionales en una tupla.

    def sumar_todo(*numeros):
        total = sum(numeros)
        print("Suma total:", total)
    
    sumar_todo(1, 2, 3)
    sumar_todo(5, 10, 15, 20)

    Salida:

    Suma total: 6  
    Suma total: 50
    ElementoTipoSignificado
    *argsTuplaAgrupa todos los argumentos posicionales
    📘 Consejo: el nombre args no es obligatorio; puedes usar *valores o cualquier otro, pero la convención es *args.

    2.5 Argumentos con nombre variable: **kwargs

    Si deseas aceptar un número variable de argumentos con nombre,
    usa **kwargs, que los agrupa en un diccionario.

    def mostrar_info(**datos):
        for clave, valor in datos.items():
            print(f"{clave}: {valor}")
    
    mostrar_info(nombre="Javier", edad=30, ciudad="Madrid")

    Salida:

    nombre: Javier  
    edad: 30  
    ciudad: Madrid
    ElementoTipoSignificado
    **kwargsDiccionarioAgrupa argumentos con nombre

    2.6 Combinación de parámetros

    Puedes combinar los distintos tipos de parámetros en una sola función, respetando el orden:

    1. Parámetros posicionales normales
    2. *args
    3. Parámetros con valor por defecto
    4. **kwargs
    def ejemplo(a, b, *args, c=10, **kwargs):
        print("a:", a)
        print("b:", b)
        print("args:", args)
        print("c:", c)
        print("kwargs:", kwargs)
    
    ejemplo(1, 2, 3, 4, 5, c=20, x=100, y=200)

    Salida:

    a: 1  
    b: 2  
    args: (3, 4, 5)  
    c: 20  
    kwargs: {'x': 100, 'y': 200}
    💡 Esta combinación ofrece máxima flexibilidad: puedes aceptar argumentos posicionales, con nombre, opcionales o incluso no definidos.

    2.7 Desempaquetado de argumentos

    También puedes pasar listas o diccionarios como argumentos usando el operador de desempaquetado:

    def mostrar(a, b, c):
        print(a, b, c)
    
    tupla = (1, 2, 3)
    mostrar(*tupla)   # Desempaqueta los valores
    
    dic = {"a": 10, "b": 20, "c": 30}
    mostrar(**dic)    # Desempaqueta las claves como argumentos con nombre

    Salida:

    1 2 3  
    10 20 30

    2.8 Buenas prácticas

    • ✅ Usa *args y **kwargs con moderación: solo cuando no conozcas el número exacto de argumentos.
    • ✅ Documenta qué tipo de parámetros espera tu función.
    • ✅ Usa nombres descriptivos (usuario, opciones, config…).
    • ✅ Evita mezclar demasiados tipos de parámetros en una sola función.

    2.9 Mini-quiz

    1. ¿Cuál es la diferencia entre *args y **kwargs?
    2. ¿Qué ocurre si defines un parámetro con valor por defecto antes de uno obligatorio?
    3. ¿Qué tipo de estructura de datos almacena *args? ¿Y **kwargs?
    💡 Respuestas
    • 1️⃣ *args agrupa argumentos posicionales; **kwargs, argumentos con nombre.
    • 2️⃣ Python genera un SyntaxError.
    • 3️⃣ *args → tupla; **kwargs → diccionario.

    2.10 Prácticas guiadas

    Ejercicio 1 — Descuento variable

    def aplicar_descuento(precio, *porcentajes):
        for p in porcentajes:
            precio -= precio * (p / 100)
        return precio
    
    print(aplicar_descuento(100, 10))
    print(aplicar_descuento(100, 10, 5))

    Ejercicio 2 — Registro dinámico

    def registrar_usuario(**datos):
        print("Registro de usuario:")
        for clave, valor in datos.items():
            print(f"  {clave}: {valor}")
    
    registrar_usuario(nombre="Ana", edad=28, ciudad="Madrid")

    Ejercicio 3 — Mezcla completa

    def resumen(nombre, *aficiones, edad=0, **otros):
        print(f"Nombre: {nombre}, Edad: {edad}")
        print("Aficiones:", aficiones)
        print("Otros datos:", otros)
    
    resumen("Javier", "Linux", "Python", "Cine", edad=30, ciudad="Madrid", profesion="Informático")



    Parte 3: Valores de Retorno y Buenas Prácticas

    En Python, las funciones pueden devolver resultados a quien las llamó mediante la palabra clave return.
    Esto permite que una función no solo ejecute instrucciones, sino también comunique información.


    3.1 ¿Qué es return?

    La instrucción return finaliza la ejecución de la función y envía un valor (o varios) al programa principal.
    Si no se especifica return, la función devuelve automáticamente None.

    def obtener_mensaje():
        return "Hola desde la función"
    
    mensaje = obtener_mensaje()
    print(mensaje)

    Salida:

    Hola desde la función
    💡 Tip: puedes usar return tantas veces como quieras, pero la función se detendrá en el primer return ejecutado.

    3.2 Funciones sin retorno explícito

    Si una función no usa return, Python devuelve None automáticamente.

    def saludar(nombre):
        print(f"Hola, {nombre}!")
    
    resultado = saludar("Javier")
    print(resultado)  # None

    Salida:

    Hola, Javier!  
    None

    En este caso, la función realiza una acción (imprimir en pantalla) pero no devuelve nada.
    Se conoce como una función de procedimiento.


    3.3 Retornar múltiples valores

    Python permite devolver más de un valor separándolos con comas.
    Internamente, esos valores se empaquetan en una tupla.

    def calcular_estadisticas(numeros):
        total = sum(numeros)
        promedio = total / len(numeros)
        minimo = min(numeros)
        maximo = max(numeros)
        return total, promedio, minimo, maximo
    
    datos = [5, 8, 2, 10]
    suma, media, menor, mayor = calcular_estadisticas(datos)
    print("Suma:", suma)
    print("Promedio:", media)
    print("Mínimo:", menor)
    print("Máximo:", mayor)

    Salida:

    Suma: 25  
    Promedio: 6.25  
    Mínimo: 2  
    Máximo: 10
    Tipo de retornoEjemploResultado
    Únicoreturn 1010
    Múltiplereturn 1, 2, 3(1, 2, 3)

    3.4 Uso de return en funciones condicionales

    Las funciones pueden devolver distintos valores según condiciones internas:

    def evaluar_edad(edad):
        if edad < 0:
            return "Edad no válida"
        elif edad < 18:
            return "Menor de edad"
        else:
            return "Mayor de edad"
    
    print(evaluar_edad(15))
    print(evaluar_edad(25))

    Salida:

    Menor de edad  
    Mayor de edad

    Este patrón es muy útil para simplificar funciones largas en lugar de usar muchas condiciones anidadas.


    3.5 Devolver estructuras de datos

    Puedes devolver listas, tuplas, diccionarios u objetos completos, no solo números o cadenas.

    def crear_usuario(nombre, edad):
        return {"nombre": nombre, "edad": edad, "activo": True}
    
    usuario = crear_usuario("Javier", 30)
    print(usuario)

    Salida:

    {'nombre': 'Javier', 'edad': 30, 'activo': True}
    📘 Consejo: devolver estructuras permite transportar más información organizada.

    3.6 Evitar print() cuando necesites resultados

    Aunque print() muestra información por pantalla, no es buena práctica usarlo dentro de funciones cuyo resultado necesites procesar.

    # Mala práctica ❌
    def sumar(a, b):
        print(a + b)
    
    # Buena práctica ✅
    def sumar(a, b):
        return a + b

    De esta forma, el valor devuelto puede reutilizarse:

    resultado = sumar(5, 7)
    doble = resultado * 2
    print("Doble del resultado:", doble)

    3.7 Buenas prácticas al devolver valores

    • ✅ Usa return solo cuando realmente necesites devolver información.
    • ✅ Evita mezclar funciones que devuelven valores con otras que solo imprimen.
    • ✅ Si una función puede fallar, devuelve un mensaje o un valor de control (por ejemplo, None o False).
    • ✅ En funciones complejas, documenta claramente qué devuelve (usa docstrings).

    3.8 Ejemplo completo

    def calcular_promedio(notas):
        """
        Calcula el promedio de una lista de notas.
        Devuelve None si la lista está vacía.
        """
        if not notas:
            return None
        return sum(notas) / len(notas)
    
    valores = [8, 9, 10]
    media = calcular_promedio(valores)
    if media is not None:
        print(f"Promedio: {media:.2f}")
    else:
        print("No hay notas para calcular")

    Salida:

    Promedio: 9.00

    3.9 Mini-quiz

    1. ¿Qué devuelve una función sin return?
    2. ¿Cómo devuelve Python varios valores?
    3. ¿Qué diferencia hay entre print() y return?
    💡 Respuestas
    • 1️⃣ Devuelve None.
    • 2️⃣ Como una tupla (aunque no la veas).
    • 3️⃣ print() muestra en pantalla; return envía datos al programa.

    3.10 Prácticas guiadas

    Ejercicio 1 — Conversor de moneda

    def convertir_eur_a_usd(euros):
        tasa = 1.07
        return euros * tasa
    
    cantidad = float(input("Euros: "))
    print("En USD:", convertir_eur_a_usd(cantidad))

    Ejercicio 2 — Calcular área y perímetro

    def area_y_perimetro_rectangulo(base, altura):
        area = base * altura
        perimetro = 2 * (base + altura)
        return area, perimetro
    
    a, p = area_y_perimetro_rectangulo(4, 3)
    print("Área:", a, "Perímetro:", p)

    Ejercicio 3 — Validar contraseña

    def validar_contrasena(texto):
        if len(texto) < 6:
            return "Demasiado corta"
        if texto.isalpha():
            return "Debe incluir números"
        if texto.isnumeric():
            return "Debe incluir letras"
        return "Contraseña válida"
    
    print(validar_contrasena("abc"))
    print(validar_contrasena("abc123"))



    Parte 4: Alcance de Variables (Scope)

    El scope o alcance define dónde puede usarse una variable dentro del programa.
    En Python, las variables pueden existir solo dentro de una función (locales) o ser accesibles desde cualquier parte del código (globales).


    4.1 ¿Qué es el alcance?

    Cuando declaras una variable dentro de una función, esa variable es local a la función.
    En cambio, si la declaras fuera de cualquier función, se considera global.

    # Variable global
    mensaje = "Hola desde el ámbito global"
    
    def mostrar():
        # Variable local
        saludo = "Hola desde la función"
        print(saludo)
    
    mostrar()
    print(mensaje)

    Salida:

    Hola desde la función  
    Hola desde el ámbito global

    En este ejemplo, saludo solo existe dentro de la función mostrar(),
    mientras que mensaje existe en todo el programa.


    4.2 Tipos de alcance en Python

    TipoDescripciónEjemplo
    LocalVariable definida dentro de una función. Solo existe allí.def f(): x = 10
    EnclosingVariable en una función envolvente (en funciones anidadas).def exterior(): def interior(): ...
    GlobalVariable definida fuera de las funciones. Visible en todo el módulo.x = 20
    Built-inVariables y funciones incorporadas por Python.len(), print(), sum()

    Python sigue un orden jerárquico de búsqueda llamado LEGB:

    • 🔸 L – Local
    • 🔸 E – Enclosing (función anidada)
    • 🔸 G – Global
    • 🔸 B – Built-in
    💡 Python busca una variable en ese orden. Si no la encuentra, lanza un NameError.

    4.3 Variables locales

    Las variables definidas dentro de una función se destruyen al terminar su ejecución.

    def mostrar_mensaje():
        mensaje = "Hola local"
        print(mensaje)
    
    mostrar_mensaje()
    print(mensaje)  # ❌ Error: variable no existe fuera de la función

    Salida:

    Hola local  
    NameError: name 'mensaje' is not defined

    4.4 Variables globales

    Las variables globales pueden ser leídas desde cualquier función,
    pero no modificadas directamente a menos que uses la palabra global.

    contador = 0
    
    def incrementar():
        global contador  # permite modificar la variable global
        contador += 1
        print("Contador:", contador)
    
    incrementar()
    incrementar()

    Salida:

    Contador: 1  
    Contador: 2
    📘 Importante: sin la palabra global, Python crea una nueva variable local con el mismo nombre.

    4.5 Alcance en funciones anidadas

    Una función puede estar dentro de otra. En ese caso, las variables de la función externa
    se consideran del tipo enclosing y pueden ser accedidas por la función interna.

    def exterior():
        mensaje = "Hola desde exterior"
    
        def interior():
            print("Interior dice:", mensaje)
    
        interior()
    
    exterior()

    Salida:

    Interior dice: Hola desde exterior

    La función interna puede leer variables de su función envolvente,
    pero no puede modificarlas directamente (a menos que uses nonlocal).


    4.6 Uso de nonlocal

    Si tienes funciones anidadas y necesitas modificar una variable del nivel superior,
    usa la palabra nonlocal.

    def exterior():
        contador = 0
    
        def interior():
            nonlocal contador
            contador += 1
            print("Contador interior:", contador)
    
        interior()
        interior()
    
    exterior()

    Salida:

    Contador interior: 1  
    Contador interior: 2

    Aquí, nonlocal indica que la variable contador pertenece al contexto
    de la función que la contiene (no global, sino de la función superior).


    4.7 Variables locales vs globales con el mismo nombre

    Si existe una variable global y otra local con el mismo nombre,
    Python usará siempre la local dentro de la función.

    mensaje = "Global"
    
    def mostrar():
        mensaje = "Local"
        print("Dentro de la función:", mensaje)
    
    mostrar()
    print("Fuera de la función:", mensaje)

    Salida:

    Dentro de la función: Local  
    Fuera de la función: Global
    ⚠️ Advertencia: tener variables con el mismo nombre en distintos alcances puede generar confusión.

    4.8 Buenas prácticas

    • ✅ Usa variables globales solo si son realmente necesarias (configuración, constantes, etc.).
    • ✅ Prefiere variables locales para mantener el código más limpio y seguro.
    • ✅ Evita depender de variables externas; pasa valores mediante parámetros.
    • ✅ Usa nonlocal y global con cuidado: pueden complicar la depuración.

    4.9 Mini-quiz

    1. ¿Qué significa el acrónimo LEGB en Python?
    2. ¿Qué palabra clave permite modificar una variable global dentro de una función?
    3. ¿Qué hace nonlocal?
    💡 Respuestas
    • 1️⃣ Local, Enclosing, Global, Built-in.
    • 2️⃣ global.
    • 3️⃣ Permite modificar variables de la función envolvente.

    4.10 Prácticas guiadas

    Ejercicio 1 — Contador global

    contador = 0
    
    def incrementar():
        global contador
        contador += 1
        print("Contador:", contador)
    
    for _ in range(3):
        incrementar()

    Ejercicio 2 — Anidamiento con nonlocal

    def fabrica_de_sumadores():
        total = 0
        def sumar(n):
            nonlocal total
            total += n
            return total
        return sumar
    
    suma = fabrica_de_sumadores()
    print(suma(5))
    print(suma(10))

    Ejercicio 3 — Variables locales y globales

    x = 100
    
    def modificar():
        x = 50
        print("Local x:", x)
    
    modificar()
    print("Global x:", x)

    Salida:

    Local x: 50  
    Global x: 100



    Parte 5: Documentación con Docstrings

    En Python, la documentación interna de las funciones se realiza mediante los
    docstrings (document strings o cadenas de documentación).
    Son una herramienta fundamental para escribir código limpio, entendible y profesional.


    5.1 ¿Qué es un docstring?

    Un docstring es una cadena de texto escrita justo después de la definición de una función,
    clase o módulo.
    Sirve para explicar qué hace, qué parámetros recibe y qué devuelve.

    def saludar(nombre):
        """Muestra un saludo personalizado por pantalla."""
        print(f"Hola, {nombre}!")

    Puedes acceder a esa documentación desde el intérprete usando la función integrada help():

    help(saludar)

    Salida:

    Help on function saludar in module __main__:
    
    saludar(nombre)
        Muestra un saludo personalizado por pantalla.

    5.2 Estructura básica de un docstring

    Según la guía PEP 257, un buen docstring debería incluir:

    • 🟩 Una línea resumen que explique brevemente la función.
    • 🟨 Una descripción más detallada opcional.
    • 🟦 Explicación de los parámetros y los valores de retorno.
    def calcular_precio_total(precio, iva):
        """
        Calcula el precio total aplicando el IVA.
    
        Parámetros:
            precio (float): Precio base del producto.
            iva (float): Porcentaje de IVA a aplicar.
    
        Retorna:
            float: Precio total con IVA incluido.
        """
        return precio * (1 + iva / 100)
    💡 Tip: El docstring debe ir siempre entre """triple comillas dobles"""
    para poder ocupar varias líneas y ser legible.

    5.3 Docstrings en funciones y clases

    Los docstrings no son solo para funciones. También pueden usarse en clases y módulos.

    """Este módulo contiene utilidades para operaciones matemáticas."""
    
    class Calculadora:
        """Representa una calculadora básica."""
    
        def sumar(self, a, b):
            """Devuelve la suma de dos números."""
            return a + b

    Los docstrings de módulos y clases ayudan a otros programadores (o a ti mismo)
    a entender el propósito general del código.


    5.4 Estilo y formato del docstring

    Aunque Python no impone un formato específico, existen estilos populares recomendados:

    EstiloEjemplo
    Google
    def area(base, altura):
        """Calcula el área de un triángulo.
        
        Args:
            base (float): Base del triángulo.
            altura (float): Altura del triángulo.
        Returns:
            float: Área calculada.
        """
    NumPy
    def multiplicar(a, b):
        """Multiplica dos números.
    
        Parameters
        ----------
        a : int or float
            Primer número.
        b : int or float
            Segundo número.
    
        Returns
        -------
        int or float
            Resultado de la multiplicación.
        """

    Ambos formatos son válidos; lo importante es ser consistente en todo el proyecto.


    5.5 Cómo acceder al docstring de forma programática

    Además de help(), puedes leer el docstring directamente con el atributo __doc__:

    def dividir(a, b):
        """Devuelve el resultado de dividir a entre b."""
        return a / b
    
    print(dividir.__doc__)

    Salida:

    Devuelve el resultado de dividir a entre b.

    5.6 Documentar funciones con *args y **kwargs

    Cuando uses *args o **kwargs, explica claramente qué tipo de datos aceptan.

    def registrar_evento(evento, *args, **kwargs):
        """
        Registra un evento en el sistema.
    
        Parámetros:
            evento (str): Nombre del evento.
            *args: Argumentos adicionales sin nombre.
            **kwargs: Argumentos con nombre (por ejemplo, usuario, fecha).
        """
        print("Evento:", evento)
        print("args:", args)
        print("kwargs:", kwargs)

    5.7 Errores comunes al documentar

    • ❌ No usar triple comillas: el docstring no será reconocido.
    • ❌ No actualizar el docstring cuando cambia la función.
    • ❌ No indicar los tipos de parámetros o el valor retornado.
    • ❌ Redactar el docstring en lenguaje confuso o con jerga innecesaria.
    ⚠️ Consejo: Mantén los docstrings actualizados siempre que cambies una función o su comportamiento.

    5.8 Buenas prácticas

    • ✅ Escribe el docstring inmediatamente después de la definición de la función o clase.
    • ✅ Usa un verbo en infinitivo o presente: “Calcula…”, “Devuelve…”, “Obtiene…”.
    • ✅ No repitas el nombre de la función en el docstring.
    • ✅ Si tu función lanza excepciones, documéntalas también.
    def dividir(a, b):
        """
        Devuelve la división de a entre b.
    
        Excepciones:
            ZeroDivisionError: Si b es igual a cero.
        """
        if b == 0:
            raise ZeroDivisionError("No se puede dividir entre cero.")
        return a / b

    5.9 Mini-quiz

    1. ¿Qué palabra se usa para crear un docstring de varias líneas?
    2. ¿Qué comando permite ver el docstring de una función?
    3. ¿Qué diferencia hay entre los estilos Google y NumPy?
    💡 Respuestas
    • 1️⃣ Triple comillas dobles """.
    • 2️⃣ La función help().
    • 3️⃣ El formato de cómo se documentan parámetros y retornos (ambos válidos).

    5.10 Prácticas guiadas

    Ejercicio 1 — Docstring simple

    def saludar(nombre):
        """Imprime un saludo con el nombre indicado."""
        print(f"Hola, {nombre}!")

    Ejercicio 2 — Docstring detallado (Google style)

    def calcular_promedio(notas):
        """Calcula el promedio de una lista de notas.
    
        Args:
            notas (list): Lista de valores numéricos.
    
        Returns:
            float: Promedio de las notas.
        """
        return sum(notas) / len(notas)

    Ejercicio 3 — Documentar función con error controlado

    def obtener_elemento(lista, indice):
        """
        Devuelve el elemento en la posición indicada.
    
        Args:
            lista (list): Lista de elementos.
            indice (int): Posición del elemento a obtener.
    
        Returns:
            any: Elemento en la posición indicada.
    
        Raises:
            IndexError: Si el índice está fuera del rango.
        """
        return lista[indice]



    Parte 6: Funciones Lambda (Funciones Anónimas)

    En Python, además de las funciones tradicionales definidas con def,
    existen las funciones lambda o funciones anónimas.
    Se denominan “anónimas” porque no necesitan un nombre y se definen en una sola línea.


    6.1 ¿Qué es una función lambda?

    Una función lambda es una forma compacta de definir pequeñas funciones
    sin necesidad de escribir una definición completa con def.
    Se usa normalmente para tareas rápidas o funciones que se pasan como argumento a otra función.

    # Sintaxis general
    lambda argumentos: expresión

    La expresión se evalúa y su resultado se devuelve automáticamente (no necesita return).

    # Ejemplo básico
    doble = lambda x: x * 2
    print(doble(5))  # 10

    Salida:

    10
    💡 Recuerda: las funciones lambda están pensadas para ser simples y expresivas.
    Si una función requiere varias líneas, mejor usa def.

    6.2 Comparación: def vs lambda

    Con defCon lambda
    def cuadrado(x):
        return x ** 2
    cuadrado = lambda x: x ** 2
    Se usa para funciones normales o complejas.Se usa para operaciones simples en una línea.
    Puede tener varias líneas y documentación.No puede incluir return ni varias instrucciones.

    6.3 Ejemplos básicos

    # Sumar dos números
    suma = lambda a, b: a + b
    print(suma(4, 6))
    
    # Obtener el último carácter de una cadena
    ultimo = lambda texto: texto[-1]
    print(ultimo("Python"))
    
    # Calcular el área de un círculo (πr²)
    import math
    area_circulo = lambda r: math.pi * r**2
    print(round(area_circulo(5), 2))

    Salida:

    10  
    n  
    78.54

    6.4 Lambdas con funciones integradas

    Las funciones lambda se usan con frecuencia junto con funciones integradas como
    map(), filter() y sorted().

    6.4.1 map()

    Aplica una función a cada elemento de un iterable (lista, tupla…).

    numeros = [1, 2, 3, 4, 5]
    cuadrados = list(map(lambda x: x**2, numeros))
    print(cuadrados)

    Salida:

    [1, 4, 9, 16, 25]

    6.4.2 filter()

    Filtra los elementos que cumplan una condición booleana.

    numeros = [10, 15, 20, 25, 30]
    pares = list(filter(lambda x: x % 2 == 0, numeros))
    print(pares)

    Salida:

    [10, 20, 30]

    6.4.3 sorted()

    Permite ordenar colecciones según una clave personalizada.

    palabras = ["python", "c", "java", "javascript"]
    ordenadas = sorted(palabras, key=lambda x: len(x))
    print(ordenadas)

    Salida:

    ['c', 'java', 'python', 'javascript']

    6.5 Lambdas con estructuras de datos

    También puedes usarlas para ordenar listas de diccionarios o aplicar cálculos rápidos sobre datos estructurados.

    usuarios = [
        {"nombre": "Ana", "edad": 28},
        {"nombre": "Javier", "edad": 35},
        {"nombre": "Lucía", "edad": 22}
    ]
    
    # Ordenar por edad
    ordenados = sorted(usuarios, key=lambda u: u["edad"])
    print(ordenados)
    
    # Obtener solo los nombres
    nombres = list(map(lambda u: u["nombre"], usuarios))
    print(nombres)

    Salida:

    [{'nombre': 'Lucía', 'edad': 22}, {'nombre': 'Ana', 'edad': 28}, {'nombre': 'Javier', 'edad': 35}]  
    ['Ana', 'Javier', 'Lucía']

    6.6 Lambdas anidadas y condicionales

    Las lambdas también pueden contener expresiones condicionales simples:

    es_par = lambda x: "Par" if x % 2 == 0 else "Impar"
    print(es_par(5))
    print(es_par(8))

    Salida:

    Impar  
    Par

    Incluso puedes anidar lambdas, aunque no es recomendable por claridad:

    aplicar = lambda f, x: f(x)
    doble = lambda n: n * 2
    print(aplicar(doble, 5))

    Salida:

    10

    6.7 Cuándo usar (y cuándo no usar) lambdas

    ✅ Úsalas cuando…🚫 Evítalas cuando…
    La función es corta y simple.La lógica tiene más de una línea o incluye if complejos.
    Solo la necesitas una vez (por ejemplo, en map o filter).Necesitas reutilizar la función varias veces.
    No necesitas documentación o nombre descriptivo.Otros desarrolladores deben entenderla fácilmente.
    📘 Consejo: usa def para funciones descriptivas y lambda para tareas puntuales.

    6.8 Buenas prácticas

    • ✅ Úsalas para expresiones cortas, especialmente en funciones como map(), filter() y sorted().
    • ✅ No abuses: si la expresión se vuelve difícil de leer, conviértela en una función normal.
    • ✅ Recuerda que no pueden contener return, bucles ni excepciones.
    • ✅ Pueden usarse dentro de otras funciones, incluso retornarlas.

    6.9 Mini-quiz

    1. ¿Qué palabra clave define una función lambda?
    2. ¿Qué devuelve una lambda si no hay return?
    3. ¿Qué función integrada aplicarías junto con lambda para filtrar elementos?
    💡 Respuestas
    • 1️⃣ La palabra lambda.
    • 2️⃣ El resultado de su expresión.
    • 3️⃣ filter().

    6.10 Prácticas guiadas

    Ejercicio 1 — Lambda simple

    # Calcular el triple de un número
    triple = lambda x: x * 3
    print(triple(7))

    Ejercicio 2 — Filtro de edades

    edades = [12, 18, 25, 16, 40]
    mayores = list(filter(lambda x: x >= 18, edades))
    print(mayores)

    Salida:

    [18, 25, 40]

    Ejercicio 3 — Ordenar por la longitud del texto

    nombres = ["Javier", "Ana", "Guillermo", "Luis"]
    ordenados = sorted(nombres, key=lambda n: len(n))
    print(ordenados)

    Ejercicio 4 — Función lambda dentro de otra función

    def aplicar_operacion(op, a, b):
        return op(a, b)
    
    resultado = aplicar_operacion(lambda x, y: x + y, 5, 7)
    print("Suma:", resultado)

    Ejercicio 5 — Combinando map() + lambda

    numeros = [1, 2, 3, 4, 5]
    cubos = list(map(lambda n: n ** 3, numeros))
    print(cubos)

    Salida:

    [1, 8, 27, 64, 125]



    Parte 7: Anotaciones de Tipo (Type Hints)

    Desde Python 3.5, el lenguaje incorpora el sistema de anotaciones de tipo, también conocidas como
    type hints.
    Estas anotaciones permiten indicar el tipo de datos que espera recibir una función y el tipo de valor que devuelve.
    No afectan la ejecución del programa, pero mejoran la legibilidad, el mantenimiento y la detección temprana de errores.


    7.1 Sintaxis básica

    Para anotar los tipos, se usa el carácter de dos puntos : después del nombre del parámetro,
    y una flecha -> antes del tipo de retorno:

    def sumar(a: int, b: int) -> int:
        return a + b

    Esto indica que sumar recibe dos enteros (int) y devuelve un entero.

    💡 Importante: las anotaciones de tipo no son obligatorias,
    pero son una práctica profesional que mejora la calidad del código.

    7.2 Tipos básicos más comunes

    TipoDescripciónEjemplo
    intNúmeros enteros5
    floatNúmeros decimales3.14
    strCadenas de texto"Python"
    boolValores booleanosTrue / False
    listListas de elementos[1, 2, 3]
    dictDiccionarios{"nombre": "Javier"}
    tupleTuplas inmutables(1, 2, 3)
    def mostrar_info(nombre: str, edad: int) -> None:
        print(f"Nombre: {nombre}, Edad: {edad}")

    El tipo None indica que la función no devuelve ningún valor útil (solo imprime o realiza acciones).


    7.3 Tipos de colecciones

    Para anotar colecciones (listas, tuplas, diccionarios…), se recomienda importar las clases de typing.

    from typing import List, Dict, Tuple
    
    def procesar_datos(numeros: List[int]) -> Tuple[int, int, float]:
        total = sum(numeros)
        minimo = min(numeros)
        promedio = total / len(numeros)
        return minimo, total, promedio
    
    resultado = procesar_datos([5, 8, 2, 10])
    print(resultado)

    Salida:

    (2, 25, 6.25)

    También puedes definir diccionarios:

    def crear_usuario(datos: Dict[str, str]) -> Dict[str, str]:
        datos["activo"] = "True"
        return datos

    7.4 Valores opcionales y el tipo Optional

    A veces una función puede devolver un valor o None.
    Para eso se usa Optional.

    from typing import Optional
    
    def buscar_usuario(id: int) -> Optional[str]:
        usuarios = {1: "Ana", 2: "Javier"}
        return usuarios.get(id)  # devuelve nombre o None
    
    print(buscar_usuario(2))
    print(buscar_usuario(10))

    Salida:

    Javier  
    None

    7.5 Anotaciones con funciones lambda

    Las funciones lambda también pueden usar anotaciones:

    doble: callable = lambda x: x * 2
    print(doble(10))

    Aunque en este caso no es obligatorio, ayuda a los editores a reconocer el tipo de función.


    7.6 Uniones de tipos (Union y |)

    Si una variable o parámetro puede ser de varios tipos, puedes usar Union (o el operador | desde Python 3.10).

    from typing import Union
    
    def mostrar(valor: Union[int, str]) -> str:
        return f"Valor: {valor}"
    
    print(mostrar(25))
    print(mostrar("Hola"))

    Salida:

    Valor: 25  
    Valor: Hola

    Equivalente en Python moderno:

    def mostrar(valor: int | str) -> str:
        return f"Valor: {valor}"

    7.7 Tipo Any

    Cuando no sabes el tipo exacto o puede variar completamente, usa Any.

    from typing import Any
    
    def procesar(valor: Any) -> None:
        print("Procesando:", valor)
    
    procesar(123)
    procesar("texto")
    procesar([1, 2, 3])

    7.8 Anotaciones de funciones que devuelven funciones

    También puedes indicar que una función devuelve otra función, usando Callable.

    from typing import Callable
    
    def crear_multiplicador(n: int) -> Callable[[int], int]:
        return lambda x: x * n
    
    duplicar = crear_multiplicador(2)
    print(duplicar(5))

    Salida:

    10

    7.9 Comprobación estática con mypy

    Python no valida los tipos en tiempo de ejecución, pero puedes usar herramientas externas como mypy para verificar el código.

    pip install mypy

    Luego ejecuta:

    mypy mi_programa.py

    Si hay inconsistencias de tipo, mypy mostrará advertencias sin detener el programa.


    7.10 Buenas prácticas

    • ✅ Usa anotaciones en funciones públicas y proyectos colaborativos.
    • ✅ Combina Optional, Union y List para mayor precisión.
    • ✅ Mantén las anotaciones simples y consistentes en todo el proyecto.
    • ✅ Utiliza mypy o pyright para comprobar errores de tipo.
    • ✅ Documenta los tipos en tus docstrings para usuarios sin soporte de type hints.

    7.11 Mini-quiz

    1. ¿Qué significa Optional[str]?
    2. ¿Qué palabra clave se usa para indicar que una función devuelve un valor?
    3. ¿Qué herramienta verifica errores de tipo en Python?
    💡 Respuestas
    • 1️⃣ Que la función puede devolver una cadena o None.
    • 2️⃣ La flecha -> seguida del tipo de retorno.
    • 3️⃣ mypy.

    7.12 Prácticas guiadas

    Ejercicio 1 — Anotaciones básicas

    def elevar(a: int, b: int) -> int:
        return a ** b
    
    print(elevar(2, 3))

    Ejercicio 2 — Listas con tipos

    from typing import List
    
    def promedio(valores: List[float]) -> float:
        return sum(valores) / len(valores)
    
    print(promedio([8.5, 9.2, 7.8]))

    Ejercicio 3 — Función que puede devolver None

    from typing import Optional
    
    def dividir(a: float, b: float) -> Optional[float]:
        if b == 0:
            return None
        return a / b
    
    print(dividir(10, 2))
    print(dividir(5, 0))

    Ejercicio 4 — Función que devuelve otra función

    from typing import Callable
    
    def crear_sumador(incremento: int) -> Callable[[int], int]:
        return lambda x: x + incremento
    
    sumar5 = crear_sumador(5)
    print(sumar5(10))

    Ejercicio 5 — Unión de tipos

    from typing import Union
    
    def formatear(valor: Union[int, float, str]) -> str:
        return f"Valor: {valor}"
    
    print(formatear(25))
    print(formatear(3.14))
    print(formatear("Hola"))

    🎯 Conclusión: las anotaciones de tipo te ayudan a escribir código más limpio,
    predecible y mantenible. Aunque no son obligatorias, cada vez son más utilizadas
    en proyectos profesionales y por herramientas de análisis estático.



    Parte 8: Resumen Final, Tablas de Referencia y Prácticas Globales

    En este módulo hemos aprendido cómo definir y utilizar funciones en Python,
    cómo manejar los parámetros y argumentos, los valores de retorno,
    el alcance de variables, la documentación con docstrings,
    las funciones lambda y las anotaciones de tipo (type hints).

    A continuación encontrarás un resumen visual con las principales ideas y un conjunto de ejercicios integradores para practicar.


    8.1 Tabla resumen general

    TemaDescripciónEjemplo
    Definición de funciónSe define con def y puede devolver valores con return.def sumar(a,b): return a+b
    Parámetros y argumentosDatos que recibe la función; pueden tener valores por defecto o ser variables.def saludar(nombre="amigo"):
    *args y **kwargsPermiten pasar un número variable de argumentos.def sumar(*n): return sum(n)
    ReturnDevuelve el resultado de una función.return resultado
    ScopeÁmbito donde existe una variable: local, global, enclosing o built-in.global x, nonlocal y
    DocstringsDocumentación interna de funciones, módulos o clases."""Calcula el área"""
    LambdaFunciones anónimas de una línea.lambda x: x*2
    Type HintsIndican los tipos de datos esperados y retornados.def sumar(a: int, b: int) -> int:

    8.2 Orden de ejecución y buenas prácticas

    • ✅ Declara tus funciones al inicio del archivo o en módulos separados.
    • ✅ Usa nombres descriptivos y minúsculas con guiones bajos (snake_case).
    • ✅ Evita funciones demasiado largas; divídelas en tareas pequeñas.
    • ✅ Documenta cada función con un docstring claro.
    • ✅ Usa return para devolver valores, no print() si necesitas reutilizarlos.
    • ✅ Usa lambdas solo para expresiones simples.
    • ✅ Incluye anotaciones de tipo en proyectos grandes.

    8.3 Ejemplo completo integrador

    Veamos cómo combinar todo lo aprendido en un mismo bloque funcional:

    from typing import List, Dict, Optional
    
    def calcular_promedios(estudiantes: List[Dict[str, float]]) -> Dict[str, float]:
        """
        Calcula el promedio de notas de cada estudiante.
    
        Args:
            estudiantes (list): Lista de diccionarios con claves 'nombre' y 'nota'.
    
        Returns:
            dict: Diccionario con el nombre y el promedio de cada estudiante.
        """
        promedios = {}
        for e in estudiantes:
            nombre = e["nombre"]
            nota = e["nota"]
            promedios[nombre] = nota
        return promedios
    
    
    def filtrar_aprobados(promedios: Dict[str, float], minimo: float = 5.0) -> Dict[str, float]:
        """Filtra estudiantes con nota igual o superior al mínimo."""
        return {n: p for n, p in promedios.items() if p >= minimo}
    
    
    def mostrar_resultados(resultados: Dict[str, float]) -> None:
        """Imprime los resultados formateados."""
        for nombre, nota in resultados.items():
            print(f"{nombre}: {nota:.2f}")
    
    
    # Programa principal
    if __name__ == "__main__":
        alumnos = [
            {"nombre": "Javier", "nota": 8.2},
            {"nombre": "Ana", "nota": 6.5},
            {"nombre": "Lucía", "nota": 4.3}
        ]
    
        promedios = calcular_promedios(alumnos)
        aprobados = filtrar_aprobados(promedios)
        mostrar_resultados(aprobados)

    Salida:

    Javier: 8.20  
    Ana: 6.50
    📘 Este ejemplo combina funciones definidas, valores de retorno, filtrado con comprensión de diccionarios, docstrings y type hints.

    8.4 Ejemplo con lambda, map y filter

    numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    # Doblar los números pares
    pares_doblados = list(map(lambda n: n * 2, filter(lambda n: n % 2 == 0, numeros)))
    print(pares_doblados)

    Salida:

    [4, 8, 12, 16]

    8.5 Prácticas globales (consolidadas)

    Ejercicio 1 — Gestión de empleados

    from typing import List, Dict
    
    def promedio_salario(empleados: List[Dict[str, float]]) -> float:
        """Devuelve el salario promedio del equipo."""
        return sum(e["salario"] for e in empleados) / len(empleados)
    
    def filtrar_por_sueldo(empleados: List[Dict[str, float]], minimo: float) -> List[str]:
        """Devuelve los nombres de empleados con sueldo superior al mínimo."""
        return [e["nombre"] for e in empleados if e["salario"] >= minimo]
    
    def mostrar_empleados(lista: List[str]) -> None:
        for nombre in lista:
            print(f"- {nombre}")
    
    empleados = [
        {"nombre": "Javier", "salario": 2400},
        {"nombre": "Ana", "salario": 2100},
        {"nombre": "Pedro", "salario": 1800}
    ]
    
    prom = promedio_salario(empleados)
    print("Salario promedio:", prom)
    mostrar_empleados(filtrar_por_sueldo(empleados, prom))

    Ejercicio 2 — Validación de contraseñas

    def validar_contrasena(texto: str) -> str:
        """Valida una contraseña según longitud y mezcla de caracteres."""
        if len(texto) < 6:
            return "Demasiado corta"
        if texto.isalpha():
            return "Debe incluir números"
        if texto.isnumeric():
            return "Debe incluir letras"
        return "Contraseña válida"
    
    print(validar_contrasena("abc"))
    print(validar_contrasena("abc123"))

    Ejercicio 3 — Composición de funciones

    from typing import Callable
    
    def aplicar_operacion(op: Callable[[int, int], int], a: int, b: int) -> int:
        """Aplica una operación binaria a dos números."""
        return op(a, b)
    
    suma = lambda x, y: x + y
    resta = lambda x, y: x - y
    print("Suma:", aplicar_operacion(suma, 5, 3))
    print("Resta:", aplicar_operacion(resta, 10, 4))

    8.6 Tabla de funciones integradas útiles en este módulo

    FunciónDescripciónEjemplo
    sum()Suma todos los elementos de una lista.sum([1,2,3]) → 6
    min()Devuelve el menor valor.min([4,7,2]) → 2
    max()Devuelve el mayor valor.max([4,7,2]) → 7
    len()Cuenta los elementos.len("Python") → 6
    map()Aplica una función a cada elemento.map(lambda x:x*2, lista)
    filter()Filtra los elementos según una condición.filter(lambda x:x>0, lista)
    sorted()Ordena los elementos de una lista.sorted(lista)
    help()Muestra la documentación de un objeto.help(print)
    type()Devuelve el tipo de un objeto.type(3.14) → float

    8.7 Conclusión del módulo

    A partir de ahora, dominas las herramientas necesarias para escribir código modular, reutilizable y bien documentado en Python.
    Las funciones son la base de todo desarrollo profesional: permiten organizar la lógica, evitar duplicaciones y aumentar la legibilidad.

    Con las anotaciones de tipo y las docstrings, tus programas estarán listos para integrarse en proyectos grandes, APIs y bibliotecas.

    🎓 Próximo paso: en el siguiente módulo aprenderás Manejo de Errores y Excepciones,
    cómo usar try, except, raise y finally para hacer tus programas más robustos.
    Nuestra puntuación
    ¡Haz clic para puntuar esta entrada!
    (Votos: 0 Promedio: 0)
    Scroll al inicio