Funciones en Python
Aprende a crear y utilizar funciones en Python para organizar tu código en bloques reutilizables, mejorando la modularidad y mantenibilidad de tus programas.
Cristian Escalante
Última actualización: 20 de mayo de 2025
Funciones en Python
Las funciones son bloques de código reutilizables diseñados para realizar una tarea específica. Son fundamentales en la programación porque permiten dividir programas complejos en piezas más pequeñas y manejables, facilitan la reutilización de código y hacen que los programas sean más legibles y mantenibles.
Definición de funciones
En Python, una función se define usando la palabra clave def
, seguida del nombre de la función y paréntesis que pueden contener parámetros:
def saludar():
print("¡Hola, mundo!")
Para ejecutar (llamar) a la función:
saludar() # Salida: ¡Hola, mundo!
Parámetros y argumentos
Las funciones pueden recibir datos a través de parámetros y devolver resultados mediante la instrucción return
.
Parámetros básicos
def saludar_persona(nombre):
print(f"¡Hola, {nombre}!")
saludar_persona("Ana") # Salida: ¡Hola, Ana!
saludar_persona("Carlos") # Salida: ¡Hola, Carlos!
Múltiples parámetros
def describir_persona(nombre, edad, ciudad):
print(f"{nombre} tiene {edad} años y vive en {ciudad}.")
describir_persona("Ana", 28, "Madrid")
# Salida: Ana tiene 28 años y vive en Madrid.
Parámetros con valores predeterminados
Puedes asignar valores predeterminados a los parámetros, que se usarán si no se proporciona un valor al llamar a la función:
def saludar(nombre, mensaje="¡Hola!"):
print(f"{mensaje} {nombre}")
saludar("Ana") # Salida: ¡Hola! Ana
saludar("Carlos", "¡Bienvenido!") # Salida: ¡Bienvenido! Carlos
Argumentos posicionales y con nombre
Puedes pasar argumentos por posición o por nombre:
def mostrar_info(nombre, edad, ciudad):
print(f"Nombre: {nombre}, Edad: {edad}, Ciudad: {ciudad}")
# Argumentos posicionales (por orden)
mostrar_info("Ana", 28, "Madrid")
# Argumentos con nombre (por nombre del parámetro)
mostrar_info(edad=28, ciudad="Madrid", nombre="Ana")
# Combinación de ambos (posicionales primero)
mostrar_info("Ana", ciudad="Madrid", edad=28)
Retorno de valores
Las funciones pueden devolver valores usando la instrucción return
. Una función puede devolver cualquier tipo de dato en Python.
Retorno simple
def sumar(a, b):
return a + b
resultado = sumar(5, 3)
print(resultado) # Salida: 8
Retorno múltiple
En Python, una función puede devolver múltiples valores como una tupla:
def estadisticas(numeros):
return min(numeros), max(numeros), sum(numeros) / len(numeros)
minimo, maximo, promedio = estadisticas([1, 2, 3, 4, 5])
print(f"Mínimo: {minimo}, Máximo: {maximo}, Promedio: {promedio}")
# Salida: Mínimo: 1, Máximo: 5, Promedio: 3.0
Retorno condicional
def valor_absoluto(numero):
if numero >= 0:
return numero
else:
return -numero
print(valor_absoluto(5)) # Salida: 5
print(valor_absoluto(-5)) # Salida: 5
Alcance de variables (Scope)
El alcance de una variable determina dónde es accesible dentro de un programa:
Variables locales
Las variables definidas dentro de una función solo son accesibles dentro de esa función:
def funcion_ejemplo():
x = 10 # Variable local
print(f"Dentro de la función: x = {x}")
funcion_ejemplo()
# print(x) # Esto generaría un error: NameError: name 'x' is not defined
Variables globales
Las variables definidas fuera de cualquier función son globales y pueden ser accedidas desde cualquier parte del programa:
y = 20 # Variable global
def mostrar_y():
print(f"Valor de y: {y}")
mostrar_y() # Salida: Valor de y: 20
Modificación de variables globales
Para modificar una variable global dentro de una función, debes declararla como global:
contador = 0
def incrementar_contador():
global contador
contador += 1
print(f"Contador: {contador}")
incrementar_contador() # Salida: Contador: 1
incrementar_contador() # Salida: Contador: 2
Funciones como objetos de primera clase
En Python, las funciones son objetos de primera clase, lo que significa que pueden ser:
Asignadas a variables
def saludar(nombre):
return f"Hola, {nombre}"
mi_funcion = saludar
print(mi_funcion("Ana")) # Salida: Hola, Ana
Pasadas como argumentos a otras funciones
def aplicar_funcion(func, valor):
return func(valor)
def duplicar(x):
return x * 2
def cuadrado(x):
return x ** 2
print(aplicar_funcion(duplicar, 5)) # Salida: 10
print(aplicar_funcion(cuadrado, 5)) # Salida: 25
Devueltas por otras funciones
def crear_multiplicador(factor):
def multiplicador(x):
return x * factor
return multiplicador
duplicar = crear_multiplicador(2)
triplicar = crear_multiplicador(3)
print(duplicar(5)) # Salida: 10
print(triplicar(5)) # Salida: 15
Funciones anónimas (lambda)
Las funciones lambda son pequeñas funciones anónimas definidas con la palabra clave lambda
:
# Sintaxis: lambda parámetros: expresión
cuadrado = lambda x: x ** 2
print(cuadrado(5)) # Salida: 25
# Uso común con funciones como map, filter, sorted
numeros = [1, 5, 2, 8, 3]
numeros_ordenados = sorted(numeros, key=lambda x: -x) # Orden descendente
print(numeros_ordenados) # Salida: [8, 5, 3, 2, 1]
Argumentos variables (*args y **kwargs)
Python permite definir funciones que acepten un número variable de argumentos:
*args (argumentos posicionales variables)
def sumar(*args):
total = 0
for numero in args:
total += numero
return total
print(sumar(1, 2)) # Salida: 3
print(sumar(1, 2, 3, 4, 5)) # Salida: 15
**kwargs (argumentos con nombre variables)
def mostrar_persona(**kwargs):
for clave, valor in kwargs.items():
print(f"{clave}: {valor}")
mostrar_persona(nombre="Ana", edad=28, ciudad="Madrid")
# Salida:
# nombre: Ana
# edad: 28
# ciudad: Madrid
Combinación de parámetros
def ejemplo(a, b, *args, c=0, **kwargs):
print(f"a = {a}, b = {b}, c = {c}")
print(f"args = {args}")
print(f"kwargs = {kwargs}")
ejemplo(1, 2, 3, 4, 5, c=10, x="hola", y="mundo")
# Salida:
# a = 1, b = 2, c = 10
# args = (3, 4, 5)
# kwargs = {'x': 'hola', 'y': 'mundo'}
Documentación de funciones (docstrings)
Es una buena práctica documentar tus funciones con docstrings:
def calcular_area_circulo(radio):
"""
Calcula el área de un círculo.
Args:
radio (float): El radio del círculo.
Returns:
float: El área del círculo.
"""
import math
return math.pi * radio ** 2
# Acceder a la documentación
print(calcular_area_circulo.__doc__)
help(calcular_area_circulo)
Anotaciones de tipo (Type Hints)
Python 3.5+ permite añadir anotaciones de tipo que ayudan a documentar y verificar el código:
def saludar(nombre: str) -> str:
return f"Hola, {nombre}"
def sumar(a: int, b: int) -> int:
return a + b
def procesar_lista(items: list[int]) -> list[int]:
return [item * 2 for item in items]
Las anotaciones de tipo no afectan al comportamiento del programa en tiempo de ejecución, pero pueden ser utilizadas por herramientas externas como mypy para verificación estática de tipos.
Funciones recursivas
Una función recursiva es aquella que se llama a sí misma:
def factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n - 1)
print(factorial(5)) # Salida: 120 (5 * 4 * 3 * 2 * 1)
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
for i in range(10):
print(fibonacci(i), end=" ") # Salida: 0 1 1 2 3 5 8 13 21 34
Decoradores
Los decoradores son funciones que modifican el comportamiento de otras funciones:
def registrar_llamada(func):
def wrapper(*args, **kwargs):
print(f"Llamando a la función: {func.__name__}")
resultado = func(*args, **kwargs)
print(f"Función {func.__name__} ejecutada")
return resultado
return wrapper
@registrar_llamada
def saludar(nombre):
print(f"Hola, {nombre}")
saludar("Ana")
# Salida:
# Llamando a la función: saludar
# Hola, Ana
# Función saludar ejecutada
Generadores
Los generadores son funciones especiales que devuelven un iterador:
def contar_hasta(n):
i = 1
while i <= n:
yield i
i += 1
for numero in contar_hasta(5):
print(numero, end=" ") # Salida: 1 2 3 4 5
Ejemplos prácticos
Ejemplo 1: Calculadora simple
def calculadora(a, b, operacion="suma"):
"""
Realiza operaciones matemáticas básicas.
Args:
a (float): Primer número.
b (float): Segundo número.
operacion (str): Operación a realizar ('suma', 'resta', 'multiplicacion', 'division').
Returns:
float: Resultado de la operación.
"""
if operacion == "suma":
return a + b
elif operacion == "resta":
return a - b
elif operacion == "multiplicacion":
return a * b
elif operacion == "division":
if b == 0:
raise ValueError("No se puede dividir entre cero")
return a / b
else:
raise ValueError(f"Operación no válida: {operacion}")
# Ejemplos de uso
print(calculadora(5, 3)) # Suma (por defecto)
print(calculadora(5, 3, operacion="resta")) # Resta
print(calculadora(5, 3, operacion="multiplicacion")) # Multiplicación
print(calculadora(5, 3, operacion="division")) # División
Ejemplo 2: Procesamiento de texto
def analizar_texto(texto):
"""
Analiza un texto y devuelve estadísticas sobre él.
Args:
texto (str): El texto a analizar.
Returns:
dict: Diccionario con estadísticas del texto.
"""
palabras = texto.split()
return {
"num_caracteres": len(texto),
"num_palabras": len(palabras),
"num_lineas": texto.count('\n') + 1,
"palabra_mas_larga": max(palabras, key=len) if palabras else "",
"longitud_promedio": sum(len(palabra) for palabra in palabras) / len(palabras) if palabras else 0
}
texto_ejemplo = """Python es un lenguaje de programación interpretado
cuya filosofía hace hincapié en la legibilidad de su código.
Es un lenguaje multiparadigma y de propósito general."""
estadisticas = analizar_texto(texto_ejemplo)
for clave, valor in estadisticas.items():
print(f"{clave}: {valor}")
Ejemplo 3: Sistema de gestión de tareas
def crear_gestor_tareas():
"""
Crea un gestor de tareas simple.
Returns:
dict: Un diccionario con funciones para gestionar tareas.
"""
tareas = []
def agregar_tarea(titulo, descripcion="", completada=False):
tarea = {
"id": len(tareas) + 1,
"titulo": titulo,
"descripcion": descripcion,
"completada": completada
}
tareas.append(tarea)
return tarea["id"]
def eliminar_tarea(id_tarea):
for i, tarea in enumerate(tareas):
if tarea["id"] == id_tarea:
return tareas.pop(i)
return None
def marcar_completada(id_tarea):
for tarea in tareas:
if tarea["id"] == id_tarea:
tarea["completada"] = True
return True
return False
def obtener_tareas(solo_pendientes=False):
if solo_pendientes:
return [tarea for tarea in tareas if not tarea["completada"]]
return tareas.copy()
return {
"agregar": agregar_tarea,
"eliminar": eliminar_tarea,
"completar": marcar_completada,
"listar": obtener_tareas
}
# Uso del gestor de tareas
gestor = crear_gestor_tareas()
# Agregar tareas
id1 = gestor["agregar"]("Comprar leche")
id2 = gestor["agregar"]("Estudiar Python", "Completar el curso de funciones")
id3 = gestor["agregar"]("Hacer ejercicio")
# Completar una tarea
gestor["completar"](id1)
# Listar tareas pendientes
tareas_pendientes = gestor["listar"](solo_pendientes=True)
print("Tareas pendientes:")
for tarea in tareas_pendientes:
print(f"- {tarea['titulo']}")
# Eliminar una tarea
gestor["eliminar"](id3)
# Listar todas las tareas
todas_tareas = gestor["listar"]()
print("\nTodas las tareas:")
for tarea in todas_tareas:
estado = "✓" if tarea["completada"] else "✗"
print(f"[{estado}] {tarea['titulo']}")
Ejemplo 4: Conversor de unidades
def crear_conversor():
"""
Crea un sistema de conversión de unidades.
Returns:
function: Una función para realizar conversiones.
"""
# Factores de conversión
conversiones = {
"longitud": {
"m_a_cm": lambda x: x * 100,
"cm_a_m": lambda x: x / 100,
"m_a_km": lambda x: x / 1000,
"km_a_m": lambda x: x * 1000,
"pies_a_m": lambda x: x * 0.3048,
"m_a_pies": lambda x: x / 0.3048
},
"peso": {
"kg_a_g": lambda x: x * 1000,
"g_a_kg": lambda x: x / 1000,
"kg_a_lb": lambda x: x * 2.20462,
"lb_a_kg": lambda x: x / 2.20462
},
"temperatura": {
"c_a_f": lambda x: x * 9/5 + 32,
"f_a_c": lambda x: (x - 32) * 5/9,
"c_a_k": lambda x: x + 273.15,
"k_a_c": lambda x: x - 273.15
}
}
def convertir(valor, tipo, conversion):
"""
Convierte un valor entre diferentes unidades.
Args:
valor (float): El valor a convertir.
tipo (str): Tipo de conversión ('longitud', 'peso', 'temperatura').
conversion (str): Conversión específica (ej: 'm_a_cm').
Returns:
float: El valor convertido.
"""
if tipo not in conversiones:
raise ValueError(f"Tipo de conversión no válido: {tipo}")
if conversion not in conversiones[tipo]:
raise ValueError(f"Conversión no válida para {tipo}: {conversion}")
return conversiones[tipo][conversion](valor)
return convertir
# Uso del conversor
conversor = crear_conversor()
# Ejemplos de conversiones
metros = 5
centimetros = conversor(metros, "longitud", "m_a_cm")
print(f"{metros} metros = {centimetros} centímetros")
fahrenheit = 98.6
celsius = conversor(fahrenheit, "temperatura", "f_a_c")
print(f"{fahrenheit}°F = {celsius:.1f}°C")
kilogramos = 70
libras = conversor(kilogramos, "peso", "kg_a_lb")
print(f"{kilogramos} kg = {libras:.2f} lb")
Mejores prácticas
1. Nombres descriptivos
Usa nombres de funciones que describan claramente lo que hacen:
# Mal
def f(x):
return x * 2
# Bien
def duplicar(numero):
return numero * 2
2. Funciones pequeñas y con un solo propósito
Cada función debe hacer una sola cosa y hacerla bien:
# Mal (hace demasiadas cosas)
def procesar_datos(datos):
# Validar datos
# Transformar datos
# Guardar datos
# Enviar notificación
pass
# Bien (separado en funciones con un solo propósito)
def validar_datos(datos):
pass
def transformar_datos(datos):
pass
def guardar_datos(datos):
pass
def enviar_notificacion(mensaje):
pass
def procesar_datos(datos):
datos_validos = validar_datos(datos)
datos_transformados = transformar_datos(datos_validos)
guardar_datos(datos_transformados)
enviar_notificacion("Datos procesados correctamente")
3. Documentación clara
Documenta tus funciones con docstrings que expliquen qué hace la función, sus parámetros y valores de retorno:
def calcular_descuento(precio, porcentaje):
"""
Calcula el descuento de un precio.
Args:
precio (float): El precio original.
porcentaje (float): El porcentaje de descuento (0-100).
Returns:
float: El precio con el descuento aplicado.
Raises:
ValueError: Si el porcentaje está fuera del rango 0-100.
"""
if not 0 <= porcentaje <= 100:
raise ValueError("El porcentaje debe estar entre 0 y 100")
factor = 1 - (porcentaje / 100)
return precio * factor
4. Manejo de errores
Anticipa posibles errores y manéjalos adecuadamente:
def dividir(a, b):
"""
Divide dos números.
Args:
a (float): Numerador.
b (float): Denominador.
Returns:
float: Resultado de la división.
Raises:
ValueError: Si el denominador es cero.
"""
if b == 0:
raise ValueError("No se puede dividir entre cero")
return a / b
5. Valores predeterminados inmutables
Usa solo objetos inmutables como valores predeterminados para evitar comportamientos inesperados:
# Mal (usa una lista mutable como valor predeterminado)
def agregar_elemento(elemento, lista=[]):
lista.append(elemento)
return lista
# Bien
def agregar_elemento(elemento, lista=None):
if lista is None:
lista = []
lista.append(elemento)
return lista
Ejercicios prácticos
- Escribe una función que determine si un número es primo.
- Crea una función que reciba una lista de números y devuelva la suma de los números pares.
- Implementa una función recursiva para calcular el máximo común divisor (MCD) de dos números utilizando el algoritmo de Euclides.
- Desarrolla una función decoradora que mida el tiempo de ejecución de cualquier otra función.
- Crea un generador que produzca los números de la secuencia de Fibonacci hasta un límite dado.
En la próxima lección, exploraremos la programación orientada a objetos en Python, que nos permitirá organizar nuestro código en clases y objetos para modelar conceptos del mundo real.