📘 Contenido del Módulo 3: Funciones y Manejo de Errores
- Parte 1 – Introducción a las Funciones en Python
- Parte 2 – Parámetros y Argumentos
- Parte 3 – Valores de Retorno y Buenas Prácticas
- Parte 4 – Alcance de Variables (Scope)
- Parte 5 – Documentación con Docstrings
- Parte 6 – Funciones Lambda (Funciones Anónimas)
- Parte 7 – Anotaciones de Tipo (Type Hints)
- Parte 8 – Resumen Final, Tablas de Referencia y Prácticas Guiadas
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.
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 ❌ |
---|---|
saludar | 3funcion |
calcular_total | def |
_imprimir_mensaje | saludar! |
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.
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
- ¿Qué palabra clave se usa para definir una función en Python?
- ¿Qué ocurre si una función no tiene
return
? - ¿Qué diferencia hay entre
print()
yreturn
?
💡 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érmino | Descripción | Ejemplo |
---|---|---|
Parámetros | Variables que se definen en la cabecera de la función. | def sumar(a, b): |
Argumentos | Valores 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 👋
# 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
Elemento | Tipo | Significado |
---|---|---|
*args | Tupla | Agrupa todos los argumentos posicionales |
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
Elemento | Tipo | Significado |
---|---|---|
**kwargs | Diccionario | Agrupa 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:
- Parámetros posicionales normales
*args
- Parámetros con valor por defecto
**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}
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
- ¿Cuál es la diferencia entre
*args
y**kwargs
? - ¿Qué ocurre si defines un parámetro con valor por defecto antes de uno obligatorio?
- ¿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
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 retorno | Ejemplo | Resultado |
---|---|---|
Único | return 10 | 10 |
Múltiple | return 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}
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
oFalse
). - ✅ 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
- ¿Qué devuelve una función sin
return
? - ¿Cómo devuelve Python varios valores?
- ¿Qué diferencia hay entre
print()
yreturn
?
💡 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
Tipo | Descripción | Ejemplo |
---|---|---|
Local | Variable definida dentro de una función. Solo existe allí. | def f(): x = 10 |
Enclosing | Variable en una función envolvente (en funciones anidadas). | def exterior(): def interior(): ... |
Global | Variable definida fuera de las funciones. Visible en todo el módulo. | x = 20 |
Built-in | Variables 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
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
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
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
yglobal
con cuidado: pueden complicar la depuración.
4.9 Mini-quiz
- ¿Qué significa el acrónimo LEGB en Python?
- ¿Qué palabra clave permite modificar una variable global dentro de una función?
- ¿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)
"""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:
Estilo | Ejemplo |
---|---|
| |
NumPy |
|
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.
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
- ¿Qué palabra se usa para crear un docstring de varias líneas?
- ¿Qué comando permite ver el docstring de una función?
- ¿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
Si una función requiere varias líneas, mejor usa
def
.6.2 Comparación: def
vs lambda
Con def | Con lambda |
---|---|
|
|
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. |
def
para funciones descriptivas y lambda
para tareas puntuales.6.8 Buenas prácticas
- ✅ Úsalas para expresiones cortas, especialmente en funciones como
map()
,filter()
ysorted()
. - ✅ 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
- ¿Qué palabra clave define una función lambda?
- ¿Qué devuelve una lambda si no hay
return
? - ¿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.
pero son una práctica profesional que mejora la calidad del código.
7.2 Tipos básicos más comunes
Tipo | Descripción | Ejemplo |
---|---|---|
int | Números enteros | 5 |
float | Números decimales | 3.14 |
str | Cadenas de texto | "Python" |
bool | Valores booleanos | True / False |
list | Listas de elementos | [1, 2, 3] |
dict | Diccionarios | {"nombre": "Javier"} |
tuple | Tuplas 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
yList
para mayor precisión. - ✅ Mantén las anotaciones simples y consistentes en todo el proyecto.
- ✅ Utiliza
mypy
opyright
para comprobar errores de tipo. - ✅ Documenta los tipos en tus
docstrings
para usuarios sin soporte de type hints.
7.11 Mini-quiz
- ¿Qué significa
Optional[str]
? - ¿Qué palabra clave se usa para indicar que una función devuelve un valor?
- ¿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
Tema | Descripción | Ejemplo |
---|---|---|
Definición de función | Se define con def y puede devolver valores con return . | def sumar(a,b): return a+b |
Parámetros y argumentos | Datos que recibe la función; pueden tener valores por defecto o ser variables. | def saludar(nombre="amigo"): |
*args y **kwargs | Permiten pasar un número variable de argumentos. | def sumar(*n): return sum(n) |
Return | Devuelve el resultado de una función. | return resultado |
Scope | Ámbito donde existe una variable: local, global, enclosing o built-in. | global x , nonlocal y |
Docstrings | Documentación interna de funciones, módulos o clases. | """Calcula el área""" |
Lambda | Funciones anónimas de una línea. | lambda x: x*2 |
Type Hints | Indican 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, noprint()
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
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ón | Descripción | Ejemplo |
---|---|---|
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.
cómo usar
try
, except
, raise
y finally
para hacer tus programas más robustos.