Saltar a contenido

Clase 7

¿Por qué necesitamos funciones?

En las clases anteriores, los programas crecieron en complejidad: ciclos, listas, validaciones, menús. Con el tiempo, aparece un problema nuevo: el código se repite.

Suponga que se necesita calcular el promedio de tres grupos de estudiantes distintos:

notas_a = [85, 90, 72, 88, 95]
suma_a = sum(notas_a)
promedio_a = suma_a / len(notas_a)
print(f"Grupo A - Promedio: {promedio_a:.1f}")

notas_b = [78, 82, 69, 91, 74]
suma_b = sum(notas_b)
promedio_b = suma_b / len(notas_b)
print(f"Grupo B - Promedio: {promedio_b:.1f}")

notas_c = [95, 88, 76, 80, 93]
suma_c = sum(notas_c)
promedio_c = suma_c / len(notas_c)
print(f"Grupo C - Promedio: {promedio_c:.1f}")

La lógica es exactamente la misma en los tres bloques. Si después se necesita cambiar el formato de salida o agregar una condición, habría que modificar el código en tres lugares distintos.

¿Qué pasa cuando hay diez grupos?

Con diez grupos, habría que duplicar la lógica diez veces. Cualquier corrección exigiría cambiar el código en diez lugares. Un solo olvido introduce un error difícil de encontrar.

Las funciones resuelven este problema.

Una función es un bloque de código con nombre que realiza una tarea específica y puede ejecutarse todas las veces que se necesite, desde cualquier parte del programa.

Con una función, el ejemplo anterior se convierte en:

def calcular_promedio(notas):
    return sum(notas) / len(notas)

notas_a = [85, 90, 72, 88, 95]
notas_b = [78, 82, 69, 91, 74]
notas_c = [95, 88, 76, 80, 93]

print(f"Grupo A - Promedio: {calcular_promedio(notas_a):.1f}")
print(f"Grupo B - Promedio: {calcular_promedio(notas_b):.1f}")
print(f"Grupo C - Promedio: {calcular_promedio(notas_c):.1f}")

La lógica se escribe una sola vez y se reutiliza tres veces. Si se necesita cambiar algo, el cambio se hace en un único lugar.

Idea central

Una función separa qué se quiere hacer (calcular un promedio) de cuándo y con qué datos hacerlo. El programador define la lógica una sola vez; la función se encarga de aplicarla cuantas veces sea necesario.

Definición y uso de funciones

Sintaxis básica

Para crear una función en Python se usa la palabra reservada def, seguida del nombre de la función y paréntesis:

def saludar(): # (1)!
    print("¡Hola mundo!") # (2)!
  1. def indica que se está definiendo una función. saludar es el nombre. Los paréntesis () son obligatorios, aunque la función no reciba datos.
  2. Todo el código indentado dentro de la función forma su cuerpo. Se ejecuta cada vez que la función es llamada.

Para ejecutar la función, se escribe su nombre seguido de paréntesis:

saludar() # (1)!
saludar() # (2)!
  1. Primera llamada: imprime ¡Hola mundo!
  2. Segunda llamada: imprime ¡Hola mundo! de nuevo.

Definir antes de llamar

La función debe definirse antes de ser llamada. Si se intenta llamar una función que aún no existe en el código, Python genera un NameError.

saludar()  # (1)!

def saludar():
    print("¡Hola!")
  1. Error: aún no está definida

Flujo de ejecución

Cuando Python encuentra una llamada a una función, pausa el código actual, ejecuta el cuerpo de la función completo y luego retoma desde donde se detuvo.

print("Antes de la función") # (1)!

def despedirse():
    print("¡Hasta luego!")

despedirse() # (2)!

print("Después de la función") # (3)!
  1. Antes de la función
  2. ¡Hasta luego!
  3. Después de la función

Nombre de una función

El nombre de una función debe describir claramente qué hace. Por convención en Python se usa snake_case: palabras en minúscula separadas por guion bajo.

Las funciones son verbos

Un buen nombre de función generalmente comienza con un verbo: calcular, mostrar, verificar, obtener, imprimir. Esto refleja que las funciones hacen algo.

Parámetros y argumentos

Una función puede recibir datos desde afuera para trabajar con ellos. Estos datos se definen como parámetros en la declaración de la función y se pasan como argumentos al llamarla.

Función con un parámetro

def saludar_estudiante(nombre): # (1)!
    print(f"¡Hola {nombre}! Bienvenido.")
  1. nombre es el parámetro: una variable local que recibe el valor que se pase al llamar la función.

Al llamar la función, se pasa el argumento entre paréntesis:

saludar_estudiante("Ana")    # (1)!
saludar_estudiante("Carlos") # (2)!
saludar_estudiante("Sofía")  # (3)!
  1. ¡Hola Ana! Bienvenido.
  2. ¡Hola Carlos! Bienvenido.
  3. ¡Hola Sofía! Bienvenido.

Función con varios parámetros

Una función puede recibir más de un parámetro, separados por comas.

def mostrar_nota(nombre, nota): # (1)!
    if nota >= 70:
        estado = "Aprobado"
    else:
        estado = "Reprobado"
    print(f"{nombre}: {nota}{estado}")
  1. La función recibe dos datos: el nombre del estudiante y su nota.
mostrar_nota("Ana", 92)     # (1)!
mostrar_nota("Luis", 65)    # (2)!
mostrar_nota("María", 78)   # (3)!
  1. Ana: 92 — Aprobado
  2. Luis: 65 — Reprobado
  3. María: 78 — Aprobado

El orden de los argumentos importa

Los argumentos se asignan a los parámetros en el mismo orden en que se declaran. Llamar mostrar_nota(92, "Ana") causaría un error porque intentaría comparar "Ana" >= 70.

Parámetros con valor por defecto

Un parámetro puede tener un valor por defecto que se usa cuando el argumento no se proporciona al llamar la función.

def mostrar_bienvenida(nombre, saludo="¡Hola"): # (1)!
    print(f"{saludo}, {nombre}!")
  1. Si no se pasa un segundo argumento, saludo tomará el valor "¡Hola" por defecto.
mostrar_bienvenida("Ana")                  # (1)!
mostrar_bienvenida("Luis", "¡Buenos días") # (2)!
  1. ¡Hola, Ana!
  2. ¡Buenos días, Luis!

Los parámetros con defecto van al final

En Python, los parámetros con valor por defecto siempre deben ir después de los parámetros sin valor por defecto. def f(a="x", b) generaría un error de sintaxis.

Retorno de valores

Hasta ahora, las funciones imprimen resultados directamente. Sin embargo, muchas veces se necesita que la función devuelva un valor para que el programa pueda usarlo después.

La instrucción return devuelve un valor al lugar desde donde se llamó la función y la termina inmediatamente.

Función con return

def calcular_promedio(notas): # (1)!
    return sum(notas) / len(notas)
  1. La función no imprime nada: devuelve el promedio calculado.

El valor devuelto puede guardarse en una variable o usarse directamente:

notas = [85, 90, 72, 88, 95]

promedio = calcular_promedio(notas)    # (1)!
print(f"El promedio es: {promedio:.1f}") # (2)!
  1. El resultado de la función se guarda en promedio.
  2. El promedio es: 86.0

También se puede usar directamente sin guardar:

print(f"El promedio es: {calcular_promedio(notas):.1f}") # (1)!
  1. El valor de retorno se usa directamente dentro del f-string.

¿return o print?

Una función que usa print solo muestra el resultado en pantalla. No puede enviarse a otra función ni guardarse para usarlo después.

Una función que usa return devuelve el resultado para que el código que la llamó decida qué hacer con él. Esto la hace mucho más flexible y reutilizable.

La regla general: las funciones deben calcular y retornar. La tarea de imprimir queda fuera de la función, en el código principal.

Múltiples puntos de retorno

Una función puede tener más de un return, aunque solo se ejecutará el primero que se alcance.

def clasificar_nota(nota):
    if nota >= 90:
        return "Excelente"
    elif nota >= 80:
        return "Muy bueno"
    elif nota >= 70:
        return "Bueno"
    else:
        return "Debe mejorar"
print(clasificar_nota(95))  # (1)!
print(clasificar_nota(82))  # (2)!
print(clasificar_nota(61))  # (3)!
  1. Excelente
  2. Muy bueno
  3. Debe mejorar

Funciones sin return

Si una función no tiene return (o tiene return sin valor), devuelve None implícitamente. None es el valor en Python que representa "ausencia de valor".

def saludar():
    print("¡Hola!")

resultado = saludar() # (1)!
print(resultado)      # (2)!
  1. La función se ejecuta e imprime ¡Hola!, pero no retorna ningún valor.
  2. None

Resumen: return en funciones

Caso Qué devuelve
return valor El valor indicado
return (sin valor) None
Sin instrucción return None

Alcance de variables

El alcance (o scope) de una variable define en qué partes del programa esa variable es accesible.

Variables locales

Una variable definida dentro de una función solo existe dentro de ella. Se crea cuando la función se ejecuta y se destruye cuando la función termina.

def calcular_area(base, altura):
    area = base * altura # (1)!
    return area

calcular_area(5, 3)
print(area)          # (2)!
  1. area es una variable local: solo existe dentro de calcular_area.
  2. NameError: name 'area' is not defined. La variable area no existe fuera de la función.

Variables globales

Una variable definida fuera de todas las funciones es global y puede leerse desde cualquier función.

umbral_aprobacion = 70  # (1)!

def aprobo(nota):
    return nota >= umbral_aprobacion  # (2)!

print(aprobo(85))  # (3)!
print(aprobo(60))  # (4)!
  1. umbral_aprobacion es una variable global: se define fuera de cualquier función.
  2. La función puede leer la variable global directamente.
  3. True
  4. False

Modificar variables globales dentro de funciones

En Python, leer una variable global dentro de una función está permitido. Pero modificarla requiere usar la palabra reservada global, lo cual no es una buena práctica.

La forma correcta es pasar los datos que la función necesita como parámetros y recibir los resultados a través de return.

# No recomendado
contador = 0
def incrementar():
    global contador
    contador += 1

# Recomendado
def incrementar(contador):
    return contador + 1

Resumen de alcance

flowchart TD
    subgraph Global["Ámbito global"]
        VG["variable_global = 10"]
        subgraph Funcion["Función mi_funcion(param)"]
            VP["param → local"]
            VL["variable_local → local"]
            LG["Puede leer variable_global"]
        end
    end

Regla práctica

Los datos que una función necesita deben entrar como parámetros y los resultados deben salir como return. Las variables globales son para constantes o configuraciones que no cambian.

Descomposición de problemas y reutilización de código

Las funciones no solo evitan repetición: son la herramienta principal para dividir un problema complejo en partes manejables.

El principio de responsabilidad única

Cada función debe hacer una sola cosa y hacerla bien. Un programa bien diseñado es una colección de funciones pequeñas, cada una con una responsabilidad clara.

Suponga que se necesita un programa que analice las notas de un grupo: muestre el promedio, identifique al mejor estudiante y cuente cuántos aprobaron.

Sin funciones, todo estaría en un solo bloque difícil de leer y mantener:

# Sin funciones — difícil de leer y mantener
nombres = ["Ana", "Luis", "María", "Carlos", "Sofía"]
notas   = [92, 78, 85, 65, 90]

suma = 0
for nota in notas:
    suma += nota
promedio = suma / len(notas)
print(f"Promedio: {promedio:.1f}")

maximo = notas[0]
indice_max = 0
for i in range(1, len(notas)):
    if notas[i] > maximo:
        maximo = notas[i]
        indice_max = i
print(f"Mejor estudiante: {nombres[indice_max]} con {maximo}")

aprobados = 0
for nota in notas:
    if nota >= 70:
        aprobados += 1
print(f"Aprobados: {aprobados}")

Con funciones, cada tarea queda aislada con un nombre claro:

def calcular_promedio(notas):
    return sum(notas) / len(notas)

def encontrar_mejor(nombres, notas):
    indice = notas.index(max(notas))
    return nombres[indice], notas[indice]

def contar_aprobados(notas, umbral=70):
    return sum(1 for nota in notas if nota >= umbral)

nombres = ["Ana", "Luis", "María", "Carlos", "Sofía"]
notas   = [92, 78, 85, 65, 90]

print(f"Promedio: {calcular_promedio(notas):.1f}")

mejor_nombre, mejor_nota = encontrar_mejor(nombres, notas)
print(f"Mejor estudiante: {mejor_nombre} con {mejor_nota}")

print(f"Aprobados: {contar_aprobados(notas)}")
Promedio: 82.0
Mejor estudiante: Ana con 92
Aprobados: 4

Ventajas de la descomposición

  • Cada función puede probarse de forma independiente.
  • Si hay un error, es más fácil localizarlo en una función pequeña que en un bloque largo.
  • Cada función puede reutilizarse en otros programas o en otras partes del mismo programa.

Funciones que llaman a otras funciones

Las funciones pueden llamarse entre sí. Esto permite construir capas de abstracción: funciones complejas construidas sobre funciones simples.

def es_par(numero):
    return numero % 2 == 0

def filtrar_pares(lista):   # (1)!
    pares = []
    for numero in lista:
        if es_par(numero):  # (2)!
            pares.append(numero)
    return pares

numeros = [1, 4, 7, 8, 12, 15, 20]
print(filtrar_pares(numeros))  # (3)!
  1. filtrar_pares depende de es_par para su lógica.
  2. Cada número se verifica con es_par antes de agregarlo.
  3. [4, 8, 12, 20]

Introducción a la recursividad

Hasta ahora, todas las funciones vistas resuelven su tarea usando ciclos o estructuras simples. Existe otro enfoque poderoso: una función que se llama a sí misma. Esto se llama recursividad.

La idea detrás de la recursividad

La recursividad es útil cuando un problema se puede expresar en términos de versiones más pequeñas del mismo problema.

Por ejemplo, el factorial de un número:

\[5! = 5 \cdot 4 \cdot 3 \cdot 2 \cdot 1 = 120\]

Pero también se puede expresar así:

\[ 5! = 5 \cdot 4!, \quad 4! = 4 \cdot 3!, \quad 3! = 3 \cdot 2!, \quad 2! = 2 \cdot 1!, \quad 1! = 1 \]

Cada factorial se define en términos del factorial anterior. El problema se va reduciendo hasta llegar a un caso conocido: \(1! = 1\).

Caso base y caso recursivo

Toda función recursiva correcta tiene dos partes obligatorias:

Parte Descripción
Caso base La condición que detiene la recursión. Sin esto, la función se llama a sí misma infinitamente.
Caso recursivo La llamada a la propia función con un problema más pequeño.

Factorial recursivo

def factorial(n):          # (1)!
    if n == 1:             # (2)!
        return 1
    return n * factorial(n - 1)  # (3)!
  1. La función recibe el número n del que se quiere calcular el factorial.
  2. Caso base: cuando n llega a 1, se retorna 1 directamente. Aquí se detiene la recursión.
  3. Caso recursivo: el factorial de n es n multiplicado por el factorial de n - 1.
print(factorial(5))  # (1)!
print(factorial(3))  # (2)!
  1. 120
  2. 6

El proceso de ejecución para factorial(5) se puede visualizar así:

flowchart TD
    A["factorial(5)"] --> B["5 × factorial(4)"]
    B --> C["4 × factorial(3)"]
    C --> D["3 × factorial(2)"]
    D --> E["2 × factorial(1)"]
    E --> F["retorna 1"]
    F --> G["retorna 2 × 1 = 2"]
    G --> H["retorna 3 × 2 = 6"]
    H --> I["retorna 4 × 6 = 24"]
    I --> J["retorna 5 × 24 = 120"]

Comparación: iterativo vs recursivo

El mismo problema puede resolverse de dos formas:

def factorial_iterativo(n):
    resultado = 1
    for i in range(2, n + 1):
        resultado *= i
    return resultado

print(factorial_iterativo(5))  # 120
def factorial_recursivo(n):
    if n == 1:
        return 1
    return n * factorial_recursivo(n - 1)

print(factorial_recursivo(5))  # 120

Ambas versiones producen el mismo resultado. La recursiva es más cercana a la definición matemática; la iterativa es más eficiente en memoria para números grandes.

El caso base es obligatorio

Sin caso base, la función se llama a sí misma indefinidamente hasta que Python detiene el programa con un RecursionError.

def factorial_roto(n):
    return n * factorial_roto(n - 1)  # Nunca termina

factorial_roto(5)  # RecursionError: maximum recursion depth exceeded

Suma recursiva de una lista

La recursividad no se limita a problemas matemáticos. Calcular la suma de una lista también puede expresarse recursivamente: la suma de una lista es el primer elemento más la suma del resto.

def suma_lista(lista):
    if len(lista) == 0:          # (1)!
        return 0
    return lista[0] + suma_lista(lista[1:])  # (2)!
  1. Caso base: una lista vacía tiene suma 0.
  2. Caso recursivo: el primer elemento más la suma del resto de la lista.
print(suma_lista([1, 2, 3, 4, 5]))  # (1)!
  1. 15

¿Cuándo usar recursividad?

La recursividad es especialmente útil cuando el problema tiene una estructura naturalmente jerárquica o repetitiva que se reduce a sí misma. Para problemas simples de iteración, los ciclos suelen ser más claros y eficientes.

Ejercicio integrador

Una estudiante de décimo año necesita un programa para analizar las temperaturas registradas durante una semana. El programa debe organizarse completamente en funciones.

El programa debe cumplir con los siguientes requisitos:

  1. Definir una función pedir_temperaturas() que solicite al usuario 7 temperaturas (una por día) y las retorne en una lista. Cada temperatura debe ser validada: debe estar entre -10 y 50 grados Celsius.
  2. Definir una función calcular_estadisticas(temps) que reciba la lista y retorne una tupla con: promedio, temperatura mínima y temperatura máxima.
  3. Definir una función clasificar_dia(temp) que reciba una temperatura y retorne una clasificación:
    • Menor a 15°C: "Frío"
    • Entre 15°C y 28°C: "Agradable"
    • Mayor a 28°C: "Caluroso"
  4. Definir una función mostrar_reporte(temps) que use clasificar_dia para imprimir la clasificación de cada día, luego muestre las estadísticas obtenidas de calcular_estadisticas.
  5. El programa principal debe llamar a pedir_temperaturas() y luego a mostrar_reporte().

Paso 1: Función para validar y pedir temperaturas

Esta función usa un ciclo while para pedir cada temperatura y valida que esté dentro del rango permitido antes de agregarla a la lista.

def pedir_temperaturas():
    dias = ["Lunes", "Martes", "Miércoles", "Jueves",
            "Viernes", "Sábado", "Domingo"]
    temperaturas = []

    for dia in dias:
        while True:
            try:
                temp = float(input(f"Temperatura del {dia} (°C): ")) # (1)!
                if temp < -10 or temp > 50:
                    print("Error: la temperatura debe estar entre -10 y 50°C.")
                else:
                    temperaturas.append(temp)
                    break
            except ValueError:
                print("Error: debe ingresar un número.")

    return temperaturas
  1. Se usa float para aceptar temperaturas con decimales como 23.5.

Paso 2: Función para calcular estadísticas

Recibe la lista de temperaturas y retorna los tres valores calculados agrupados en una tupla.

def calcular_estadisticas(temps):
    promedio = sum(temps) / len(temps)
    minima   = min(temps)
    maxima   = max(temps)
    return promedio, minima, maxima # (1)!
  1. Python permite retornar múltiples valores separados por coma. El resultado es una tupla que puede desempaquetarse al recibirla.

Paso 3: Función para clasificar un día

Recibe una sola temperatura y retorna su categoría como string.

def clasificar_dia(temp):
    if temp < 15:
        return "Frío"
    elif temp <= 28:
        return "Agradable"
    else:
        return "Caluroso"

Paso 4: Función para mostrar el reporte

Esta función usa las otras funciones: llama a clasificar_dia para cada temperatura y a calcular_estadisticas para las estadísticas finales.

def mostrar_reporte(temps):
    dias = ["Lunes", "Martes", "Miércoles", "Jueves",
            "Viernes", "Sábado", "Domingo"]

    print("\n--- Reporte semanal ---")
    for i, temp in enumerate(temps):            # (1)!
        clasificacion = clasificar_dia(temp)
        print(f"{dias[i]}: {temp:.1f}°C — {clasificacion}")

    promedio, minima, maxima = calcular_estadisticas(temps) # (2)!
    print(f"\nPromedio semanal: {promedio:.1f}°C")
    print(f"Temperatura mínima: {minima:.1f}°C")
    print(f"Temperatura máxima: {maxima:.1f}°C")
  1. enumerate entrega el índice y el valor en cada iteración, lo que permite acceder al nombre del día correspondiente.
  2. Los tres valores de la tupla se desempaquetan en tres variables separadas.

Programa completo

def pedir_temperaturas():
    dias = ["Lunes", "Martes", "Miércoles", "Jueves",
            "Viernes", "Sábado", "Domingo"]
    temperaturas = []

    for dia in dias:
        while True:
            try:
                temp = float(input(f"Temperatura del {dia} (°C): "))
                if temp < -10 or temp > 50:
                    print("Error: la temperatura debe estar entre -10 y 50°C.")
                else:
                    temperaturas.append(temp)
                    break
            except ValueError:
                print("Error: debe ingresar un número.")

    return temperaturas


def calcular_estadisticas(temps):
    promedio = sum(temps) / len(temps)
    minima   = min(temps)
    maxima   = max(temps)
    return promedio, minima, maxima


def clasificar_dia(temp):
    if temp < 15:
        return "Frío"
    elif temp <= 28:
        return "Agradable"
    else:
        return "Caluroso"


def mostrar_reporte(temps):
    dias = ["Lunes", "Martes", "Miércoles", "Jueves",
            "Viernes", "Sábado", "Domingo"]

    print("\n--- Reporte semanal ---")
    for i, temp in enumerate(temps):
        clasificacion = clasificar_dia(temp)
        print(f"{dias[i]}: {temp:.1f}°C — {clasificacion}")

    promedio, minima, maxima = calcular_estadisticas(temps)
    print(f"\nPromedio semanal: {promedio:.1f}°C")
    print(f"Temperatura mínima: {minima:.1f}°C")
    print(f"Temperatura máxima: {maxima:.1f}°C")


# Programa principal
temperaturas = pedir_temperaturas()
mostrar_reporte(temperaturas)

Ejemplos de ejecución

Temperatura del Lunes (°C): 22
Temperatura del Martes (°C): 18
Temperatura del Miércoles (°C): 30
Temperatura del Jueves (°C): 27
Temperatura del Viernes (°C): 14
Temperatura del Sábado (°C): 12
Temperatura del Domingo (°C): 25

--- Reporte semanal ---
Lunes: 22.0°C — Agradable
Martes: 18.0°C — Agradable
Miércoles: 30.0°C — Caluroso
Jueves: 27.0°C — Agradable
Viernes: 14.0°C — Frío
Sábado: 12.0°C — Frío
Domingo: 25.0°C — Agradable

Promedio semanal: 21.1°C
Temperatura mínima: 12.0°C
Temperatura máxima: 30.0°C
Temperatura del Lunes (°C): hola
Error: debe ingresar un número.
Temperatura del Lunes (°C): 200
Error: la temperatura debe estar entre -10 y 50°C.
Temperatura del Lunes (°C): 22
Temperatura del Martes (°C): ...

Ejercicios adicionales

Como funciones es un tema tan amplio, a continuación se muestran 10 ejercicios resueltos para practicar los conceptos vistos hasta el momento.

1. Clasificar triángulo

Escriba dos funciones: es_valido(a, b, c) que verifique si tres lados forman un triángulo (la suma de cualquier par de lados debe ser mayor que el tercero), y clasificar_triangulo(a, b, c) que retorne "Equilátero", "Isósceles", "Escaleno" o "Inválido" según corresponda. El programa debe pedir los tres lados al usuario.

def es_valido(a, b, c):
    return a + b > c and a + c > b and b + c > a

def clasificar_triangulo(a, b, c):
    if not es_valido(a, b, c):
        return "Inválido"
    if a == b == c:
        return "Equilátero"
    if a == b or b == c or a == c:
        return "Isósceles"
    return "Escaleno"

a = float(input("Lado a: "))
b = float(input("Lado b: "))
c = float(input("Lado c: "))
print(clasificar_triangulo(a, b, c))

Observe el uso de una función que retorna un booleano como condición dentro de un if. Esta es una práctica común.

Ejemplos de ejecución

Lado a: 5
Lado b: 5
Lado c: 3
Isósceles
Lado a: 1
Lado b: 2
Lado c: 10
Inválido

2. Fibonacci recursivo

Escriba una función recursiva fibonacci(n) que retorne el n-ésimo número de la secuencia de Fibonacci, donde fibonacci(0) = 0 y fibonacci(1) = 1. El programa debe pedir un número al usuario e imprimir el resultado.

La secuencia comienza: 0, 1, 1, 2, 3, 5, 8, 13, 21, …

def fibonacci(n):
    if n == 0:                              # (1)!
        return 0
    if n == 1:                              # (2)!
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)  # (3)!

n = int(input("Posición en la secuencia: "))
print(f"Fibonacci({n}) = {fibonacci(n)}")
  1. Caso base: el término 0 es 0.
  2. Caso base: el término 1 es 1.
  3. Caso recursivo: cada término es la suma de los dos anteriores.

Ejemplos de ejecución

Posición en la secuencia: 7
Fibonacci(7) = 13
Posición en la secuencia: 0
Fibonacci(0) = 0

3. Contar vocales

Escriba una función contar_vocales(texto) que reciba una cadena de texto y retorne la cantidad de vocales que contiene (sin distinguir mayúsculas de minúsculas). El programa debe pedir una oración al usuario e imprimir el resultado.

def contar_vocales(texto):
    vocales = "aeiouáéíóúAEIOUÁÉÍÓÚ"
    contador = 0
    for caracter in texto:      # (1)!
        if caracter in vocales:
            contador += 1
    return contador

oracion = input("Ingrese una oración: ")
print(f"Cantidad de vocales: {contar_vocales(oracion)}")
  1. Se recorre la cadena carácter por carácter y se verifica si pertenece al string de vocales.

Ejemplos de ejecución

Ingrese una oración: Hola mundo
Cantidad de vocales: 4
Ingrese una oración: Murciélago
Cantidad de vocales: 5

4. Estadísticas de una lista

Escriba una función estadisticas(lista) que reciba una lista de números y retorne una tupla con tres valores: el promedio, el mínimo y el máximo. El programa debe pedir al usuario 5 números, llamar a la función e imprimir los tres resultados.

def estadisticas(lista):
    promedio = sum(lista) / len(lista)
    return promedio, min(lista), max(lista)  # (1)!

numeros = []
for i in range(5):
    numeros.append(float(input(f"Número {i + 1}: ")))

promedio, minimo, maximo = estadisticas(numeros)  # (2)!
print(f"Promedio: {promedio:.2f}")
print(f"Mínimo: {minimo}")
print(f"Máximo: {maximo}")
  1. Se retornan tres valores separados por coma; Python los empaqueta automáticamente en una tupla.
  2. Los tres valores se desempaquetan en tres variables al recibirlos.

Ejemplo de ejecución

Número 1: 10
Número 2: 4
Número 3: 7
Número 4: 15
Número 5: 3
Promedio: 7.80
Mínimo: 3.0
Máximo: 15.0

5. Validar contraseña

Escriba una función validar_contrasena(clave) que verifique si una contraseña cumple todas las siguientes reglas y retorne una lista de errores (vacía si es válida):

  1. Tiene al menos 8 caracteres.
  2. Contiene al menos una letra mayúscula.
  3. Contiene al menos un dígito.

El programa debe pedir la contraseña e informar si es válida o cuáles reglas no se cumplen.

def validar_contrasena(clave):
    errores = []
    if len(clave) < 8:
        errores.append("Debe tener al menos 8 caracteres.")
    if not any(c.isupper() for c in clave):     # (1)!
        errores.append("Debe contener al menos una mayúscula.")
    if not any(c.isdigit() for c in clave):     # (2)!
        errores.append("Debe contener al menos un dígito.")
    return errores

clave = input("Ingrese una contraseña: ")
errores = validar_contrasena(clave)

if not errores:
    print("Contraseña válida.")
else:
    for error in errores:
        print(f"- {error}")
  1. any retorna True si al menos un carácter cumple la condición; isupper() verifica si es mayúscula.
  2. isdigit() verifica si el carácter es un dígito del 0 al 9.

Ejemplos de ejecución

Ingrese una contraseña: Colegio2025
Contraseña válida.
Ingrese una contraseña: hola
- Debe tener al menos 8 caracteres.
- Debe contener al menos una mayúscula.
- Debe contener al menos un dígito.

6. Tabla de multiplicar

Escriba una función tabla_multiplicar(n, limite=10) que imprima la tabla de multiplicar del número n desde 1 hasta limite. El parámetro limite debe tener valor por defecto de 10. El programa debe pedir el número al usuario y, opcionalmente, hasta qué múltiplo mostrar.

def tabla_multiplicar(n, limite=10):
    for i in range(1, limite + 1):
        print(f"{n} × {i} = {n * i}")

n = int(input("Número: "))
respuesta = input("¿Hasta qué múltiplo? (Enter para usar 10): ")

if respuesta == "":         # (1)!
    tabla_multiplicar(n)
else:
    tabla_multiplicar(n, int(respuesta))
  1. Si el usuario no ingresa nada, se usa el valor por defecto del parámetro limite.

Ejemplos de ejecución

Número: 7
¿Hasta qué múltiplo? (Enter para usar 10):
7 × 1 = 7
7 × 2 = 14
...
7 × 10 = 70
Número: 3
¿Hasta qué múltiplo? (Enter para usar 10): 5
3 × 1 = 3
3 × 2 = 6
3 × 3 = 9
3 × 4 = 12
3 × 5 = 15

7. Potencia recursiva

Escriba una función recursiva potencia(base, exp) que calcule base elevado a exp sin usar el operador **. El exponente siempre será un entero no negativo. El programa debe pedir ambos valores al usuario.

Pista: base^0 = 1 y base^n = base × base^(n-1).

def potencia(base, exp):
    if exp == 0:                               # (1)!
        return 1
    return base * potencia(base, exp - 1)     # (2)!

base = float(input("Base: "))
exp  = int(input("Exponente (entero ≥ 0): "))
print(f"{base} ^ {exp} = {potencia(base, exp)}")
  1. Caso base: cualquier número elevado a 0 es 1.
  2. Caso recursivo: base^n es base multiplicado por base^(n-1).

Ejemplos de ejecución

Base: 2
Exponente (entero ≥ 0): 10
2.0 ^ 10 = 1024.0
Base: 5
Exponente (entero ≥ 0): 0
5.0 ^ 0 = 1

8. Verificar palíndromo

Escriba una función es_palindromo(palabra) que retorne True si la palabra es un palíndromo (se lee igual de izquierda a derecha que de derecha a izquierda) y False en caso contrario. La función debe ignorar mayúsculas y minúsculas. El programa debe pedir una palabra al usuario e informar el resultado.

def es_palindromo(palabra):
    palabra = palabra.lower()           # (1)!
    return palabra == palabra[::-1]     # (2)!

palabra = input("Ingrese una palabra: ")

if es_palindromo(palabra):
    print(f'"{palabra}" es un palíndromo.')
else:
    print(f'"{palabra}" no es un palíndromo.')
  1. Se convierte a minúsculas para que la comparación no distinga entre A y a.
  2. [::-1] invierte la cadena; si es igual a sí misma invertida, es un palíndromo.

Ejemplos de ejecución

Ingrese una palabra: Reconocer
"Reconocer" es un palíndromo.
Ingrese una palabra: Python
"Python" no es un palíndromo.

9. Análisis de calificaciones

Escriba un programa con tres funciones para analizar las notas de un grupo de estudiantes:

  1. pedir_notas(cantidad) — solicita cantidad notas al usuario (entre 0 y 100) y las retorna en una lista.
  2. clasificar(nota) — retorna "Aprobado" si la nota es mayor o igual a 70, o "Reprobado" en caso contrario.
  3. mostrar_reporte(notas) — imprime cada nota con su clasificación, el promedio del grupo y cuántos aprobaron.
def pedir_notas(cantidad):
    notas = []
    for i in range(cantidad):
        continuar = True
        while continuar:
            try:
                nota = float(input(f"Nota {i + 1}: "))
                if 0 <= nota <= 100:
                    notas.append(nota)
                    continuar = False
                else:
                    print("La nota debe estar entre 0 y 100.")
            except ValueError:
                print("Ingrese un número válido.")
    return notas

def clasificar(nota):
    return "Aprobado" if nota >= 70 else "Reprobado"

def mostrar_reporte(notas):
    print("\n--- Reporte del grupo ---")
    aprobados = 0
    for i, nota in enumerate(notas):
        estado = clasificar(nota)           # (1)!
        if estado == "Aprobado":
            aprobados += 1
        print(f"Estudiante {i + 1}: {nota:.1f}{estado}")
    print(f"\nPromedio: {sum(notas) / len(notas):.1f}")
    print(f"Aprobados: {aprobados} de {len(notas)}")

cantidad = int(input("¿Cuántos estudiantes? "))
notas = pedir_notas(cantidad)
mostrar_reporte(notas)
  1. mostrar_reporte delega la clasificación de cada nota a clasificar, que tiene una sola responsabilidad.

Ejemplo de ejecución

¿Cuántos estudiantes? 4
Nota 1: 85
Nota 2: 62
Nota 3: 70
Nota 4: 45

--- Reporte del grupo ---
Estudiante 1: 85.0 — Aprobado
Estudiante 2: 62.0 — Reprobado
Estudiante 3: 70.0 — Aprobado
Estudiante 4: 45.0 — Reprobado

Promedio: 65.5
Aprobados: 2 de 4

10. Suma de dígitos

Escriba una función recursiva suma_digitos(n) que reciba un número entero positivo y retorne la suma de todos sus dígitos. El programa debe pedir un número al usuario e imprimir el resultado.

Por ejemplo: suma_digitos(1234)1 + 2 + 3 + 4 = 10.

def suma_digitos(n):
    if n < 10:                              # (1)!
        return n
    return n % 10 + suma_digitos(n // 10)  # (2)!

n = int(input("Ingrese un número entero positivo: "))
print(f"Suma de dígitos de {n}: {suma_digitos(n)}")
  1. Caso base: un número de un solo dígito es él mismo.
  2. Caso recursivo: el último dígito (n % 10) más la suma de los dígitos restantes (n // 10 elimina el último dígito).

Ejemplos de ejecución

Ingrese un número entero positivo: 1234
Suma de dígitos de 1234: 10
Ingrese un número entero positivo: 999
Suma de dígitos de 999: 27