Las funciones en Python son bloques de código reutilizables que estructuran los programas y los hacen mantenibles. Dominar funciones, parámetros, recursividad y closures es clave para superar entrevistas técnicas y escribir código de calidad profesional. Aquí tienes 20 ejercicios de Python con funciones resueltos, todos con código funcional y explicación.

Funciones básicas

Ejercicio 1 — Saludo personalizado con valor por defecto

def saludar(nombre, saludo="Hola"):
    return f"{saludo}, {nombre}!"

print(saludar("Ana"))                   # Hola, Ana!
print(saludar("Luis", "Buenos dias"))   # Buenos dias, Luis!

Ejercicio 2 — Calculadora básica

def calcular(a, b, operacion="suma"):
    operaciones = {
        "suma": a + b,
        "resta": a - b,
        "multiplicacion": a * b,
        "division": a / b if b != 0 else "Error: division por cero"
    }
    return operaciones.get(operacion, "Operacion no valida")

print(calcular(10, 3, "resta"))  # 7

Ejercicio 3 — Factorial iterativo

def factorial(n):
    if n < 0:
        return None
    resultado = 1
    for i in range(2, n + 1):
        resultado *= i
    return resultado

print(factorial(5))  # 120

Ejercicio 4 — Fibonacci con caché

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print([fibonacci(i) for i in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Funciones con *args y **kwargs

Estos parámetros especiales permiten recibir un número variable de argumentos, algo muy habitual en librerías y frameworks Python.

Ejercicio 5 — Suma de argumentos variables

def sumar(*numeros):
    return sum(numeros)

print(sumar(1, 2, 3))        # 6
print(sumar(10, 20, 30, 40)) # 100

Ejercicio 6 — Perfil de usuario con kwargs

def crear_perfil(nombre, **datos):
    perfil = {"nombre": nombre}
    perfil.update(datos)
    return perfil

p = crear_perfil("Carlos", edad=28, ciudad="Madrid", rol="desarrollador")
print(p)
# {"nombre": "Carlos", "edad": 28, "ciudad": "Madrid", "rol": "desarrollador"}

Funciones lambda

Las funciones lambda son funciones anónimas de una sola expresión. Son muy útiles con sorted(), map() y filter().

Ejercicio 7 — Ordenar por longitud

palabras = ["Python", "es", "un", "lenguaje", "genial"]
ordenadas = sorted(palabras, key=lambda x: len(x))
print(ordenadas)  # ["es", "un", "Python", "genial", "lenguaje"]

Ejercicio 8 — Map y filter con lambda

numeros = range(1, 21)
pares_al_cuadrado = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, numeros)))
print(pares_al_cuadrado)
# [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]

Funciones recursivas

Ejercicio 9 — Búsqueda binaria recursiva

def busqueda_binaria(lista, objetivo, inicio=0, fin=None):
    if fin is None:
        fin = len(lista) - 1
    if inicio > fin:
        return -1
    medio = (inicio + fin) // 2
    if lista[medio] == objetivo:
        return medio
    elif lista[medio] < objetivo:
        return busqueda_binaria(lista, objetivo, medio + 1, fin)
    else:
        return busqueda_binaria(lista, objetivo, inicio, medio - 1)

nums = [1, 3, 5, 7, 9, 11, 13]
print(busqueda_binaria(nums, 7))  # 3

Ejercicio 10 — Torres de Hanoi

def hanoi(n, origen, destino, auxiliar):
    if n == 1:
        print(f"Mover disco 1 de {origen} a {destino}")
        return
    hanoi(n - 1, origen, auxiliar, destino)
    print(f"Mover disco {n} de {origen} a {destino}")
    hanoi(n - 1, auxiliar, destino, origen)

hanoi(3, "A", "C", "B")

Closures y decoradores básicos

Ejercicio 11 — Contador con closure

def hacer_contador(inicio=0):
    cuenta = [inicio]
    def incrementar(paso=1):
        cuenta[0] += paso
        return cuenta[0]
    return incrementar

contador = hacer_contador(10)
print(contador())   # 11
print(contador(5))  # 16

Ejercicio 12 — Decorador de tiempo de ejecución

import time
import functools

def medir_tiempo(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        inicio = time.time()
        resultado = func(*args, **kwargs)
        fin = time.time()
        print(f"{func.__name__} tardo {fin - inicio:.4f}s")
        return resultado
    return wrapper

@medir_tiempo
def operacion_lenta():
    time.sleep(0.1)
    return "Listo"

operacion_lenta()

8 ejercicios adicionales para practicar

Amplia tu dominio de funciones avanzadas en Python con estos retos:

  1. Implementa una función que valide si un email tiene formato correcto usando solo lógica de strings.
  2. Crea un generador (yield) que produzca números de Fibonacci indefinidamente.
  3. Escribe una función que aplique otra función N veces a un valor inicial.
  4. Implementa map() y filter() desde cero sin usar los built-ins.
  5. Crea un decorador que registre en log cada llamada a la función con sus argumentos.
  6. Escribe una función que convierta números romanos a enteros.
  7. Implementa flatten() para listas anidadas de cualquier profundidad.
  8. Crea una función memoize() genérica sin usar lru_cache.

Preguntas frecuentes sobre funciones en Python

¿Cuál es la diferencia entre *args y **kwargs en Python?

*args permite pasar un número variable de argumentos posicionales, que la función recibe como una tupla. **kwargs permite pasar argumentos con nombre en cantidad variable, recibidos como un diccionario. Puedes combinar ambos: def funcion(*args, **kwargs). El nombre args y kwargs es solo una convención; lo importante son los asteriscos.

¿Qué es una función lambda y cuándo usarla?

Una función lambda es una función anónima de una sola expresión definida con la palabra clave lambda. Son ideales cuando necesitas una función simple de forma puntual, especialmente como argumento de sorted(), map() o filter(). Ejemplo: cuadrado = lambda x: x**2. Para funciones que necesiten más lógica o un nombre reutilizable, usa def.

¿Cuándo conviene usar recursividad en Python?

La recursividad es ideal cuando el problema tiene una estructura naturalmente recursiva: recorrido de árboles, algoritmos divide y vencerás (quicksort, mergesort) o cálculo de factorial y Fibonacci. En Python el límite por defecto de recursión es 1000 llamadas. Para problemas de gran profundidad, prefiere soluciones iterativas o activa la memorización con @lru_cache.

¿Qué es un closure en Python?

Un closure es una función interna que recuerda el estado del ámbito donde fue creada, incluso después de que ese ámbito haya terminado. Se forman cuando una función interna accede a variables de la función externa y esta devuelve la función interna. Son la base de los decoradores y permiten crear funciones con estado sin necesidad de clases.

Conclusión

Las funciones son el corazón de la programación en Python. Dominando parámetros, lambdas, recursividad y closures estarás listo para afrontar cualquier reto. Continúa practicando con los ejercicios de Python con listas y los ejercicios generales de Python.