HDP115

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.

CE

Cristian Escalante

Última actualización: 20 de mayo de 2025

python
programación
modularidad

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

  1. Escribe una función que determine si un número es primo.
  2. Crea una función que reciba una lista de números y devuelva la suma de los números pares.
  3. Implementa una función recursiva para calcular el máximo común divisor (MCD) de dos números utilizando el algoritmo de Euclides.
  4. Desarrolla una función decoradora que mida el tiempo de ejecución de cualquier otra función.
  5. 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.

Bucles en Python
Aprende a utilizar estructuras de repetición como for y whil...
Manejo de excepciones
Aprende a controlar errores y situaciones inesperadas en tus...

Conceptos Básicos de HTML

Aprende los conceptos básicos de HTML

Conceptos Básicos de CSS

Aprende los conceptos básicos de CSS

Conceptos Básicos de JavaScript

Aprende los conceptos básicos de JavaScript

Conceptos Básicos SQL

Aprende los conceptos básicos de SQL

Conceptos Básicos de GIT

Aprende los conceptos básicos de GIT

Conceptos Básicos de UML

Aprende los conceptos básicos de UML

Refuerzo Academico de Herramientas de Productividad 2025