De una entrada a un resultado

10 ejercicios para entender las funciones en Python

Escribe funciones que puedas comprobar. Cada reto trae un contrato claro, casos de prueba, una pista y una solución explicada que tú decides cuándo abrir.

10 retos progresivosPistas y solucionesPruebas con assert

Cómo practicar con estos ejercicios de funciones

Necesitas conocer variables, condiciones, bucles y la sintaxis de def. Si todavía no has llegado a ese punto, empieza por las lecciones de funciones y parámetros y retorno.

  1. Lee la entrada y el resultado que exige el ejercicio.
  2. Escribe la función y añade debajo las comprobaciones.
  3. Ejecuta ambas partes. Un assert que pasa no imprime nada; si falla, aparece AssertionError.
  4. Antes de abrir la solución, prueba con un caso nuevo y explica por qué debe funcionar.
El contrato manda.

Las funciones deben devolver resultados con return. Mostrar un resultado con print no sustituye el valor que espera recibir quien llama a la función.

Las soluciones incluyen sus pruebas y se pueden abrir directamente en el editor. Las comprobaciones son educativas: no sustituyen toda la validación que necesitaría una aplicación real.

Nivel 1

Parámetros y return

Ejercicio 01

Convierte Celsius a Fahrenheit

Escribe celsius_a_fahrenheit(celsius). Recibe un número y devuelve su temperatura en Fahrenheit con la fórmula celsius × 9 / 5 + 32. Acepta temperaturas negativas.

Comprobaciones · añade tu función antes
assert celsius_a_fahrenheit(0) == 32
assert celsius_a_fahrenheit(100) == 212
assert celsius_a_fahrenheit(-40) == -40
Ver una pista para el ejercicio 1

La fórmula es toda la transformación. No necesitas input ni print dentro de la función.

Ver solución y explicación del ejercicio 1
Solución completa · ejercicio 01
def celsius_a_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

assert celsius_a_fahrenheit(0) == 32
assert celsius_a_fahrenheit(100) == 212
assert celsius_a_fahrenheit(-40) == -40
print("Pruebas superadas")

El parámetro representa la entrada y return entrega el cálculo. El caso de −40 comprueba que no estás restringiendo por error las temperaturas a valores positivos.

Ejercicio 02

Devuelve si un número es par

Escribe es_par(numero). Recibe un entero y devuelve un booleano: True si es divisible por dos y False en caso contrario. El cero y los enteros negativos también cuentan.

Comprobaciones · añade tu función antes
assert es_par(8) is True
assert es_par(7) is False
assert es_par(0) is True
assert es_par(-4) is True
Ver una pista para el ejercicio 2

El operador % obtiene el resto de una división. Compara ese resto con cero.

Ver solución y explicación del ejercicio 2
Solución completa · ejercicio 02
def es_par(numero):
    return numero % 2 == 0

assert es_par(8) is True
assert es_par(7) is False
assert es_par(0) is True
assert es_par(-4) is True
print("Pruebas superadas")

La comparación ya produce True o False. Devolver las cadenas "True" y "False" no cumpliría el contrato: el resultado debe ser un booleano.

Ejercicio 03

Saluda con un parámetro opcional

Escribe saludar(nombre="invitado"). Recibe texto, elimina espacios al principio y al final y devuelve "Hola, nombre". Si el texto queda vacío, usa "invitado".

Comprobaciones · añade tu función antes
assert saludar(" Ana ") == "Hola, Ana"
assert saludar() == "Hola, invitado"
assert saludar("   ") == "Hola, invitado"
Ver una pista para el ejercicio 3

Combina un valor predeterminado, strip y una condición para el nombre vacío.

Ver solución y explicación del ejercicio 3
Solución completa · ejercicio 03
def saludar(nombre="invitado"):
    limpio = nombre.strip()
    if not limpio:
        limpio = "invitado"
    return f"Hola, {limpio}"

assert saludar(" Ana ") == "Hola, Ana"
assert saludar() == "Hola, invitado"
assert saludar("   ") == "Hola, invitado"
print("Pruebas superadas")

El valor predeterminado solo actúa cuando no pasas un argumento. La condición adicional resuelve otro caso: un argumento presente que contiene únicamente espacios.

Ejercicio 04

Limita un valor a un intervalo

Escribe limitar(valor, minimo, maximo). Devuelve minimo si valor queda por debajo, maximo si queda por encima y valor si está dentro. Para este ejercicio, las entradas son números y minimo nunca supera maximo.

Comprobaciones · añade tu función antes
assert limitar(-2, 0, 10) == 0
assert limitar(15, 0, 10) == 10
assert limitar(4, 0, 10) == 4
assert limitar(3, 3, 3) == 3
Ver una pista para el ejercicio 4

Comprueba cada extremo en una rama. Si ninguna condición se cumple, devuelve el valor original.

Ver solución y explicación del ejercicio 4
Solución completa · ejercicio 04
def limitar(valor, minimo, maximo):
    if valor < minimo:
        return minimo
    if valor > maximo:
        return maximo
    return valor

assert limitar(-2, 0, 10) == 0
assert limitar(15, 0, 10) == 10
assert limitar(4, 0, 10) == 4
assert limitar(3, 3, 3) == 3
print("Pruebas superadas")

Cada rama termina con return. El último retorno cubre los valores del intervalo, incluidos los extremos. Una ampliación sería rechazar intervalos invertidos de forma explícita.

Nivel 2

Colecciones y casos vacíos

Ejercicio 05

Cuenta vocales en un texto

Escribe contar_vocales(texto). Cuenta las apariciones de a, e, i, o y u sin distinguir mayúsculas. En este contrato, las vocales acentuadas no se incluyen. Un texto vacío devuelve cero.

Comprobaciones · añade tu función antes
assert contar_vocales("Hola PYTHON") == 3
assert contar_vocales("AEIOU") == 5
assert contar_vocales("") == 0
assert contar_vocales("áéíóú") == 0
Ver una pista para el ejercicio 5

Normaliza el texto con lower, recorre sus caracteres y acumula uno cuando el carácter esté en "aeiou".

Ver solución y explicación del ejercicio 5
Solución completa · ejercicio 05
def contar_vocales(texto):
    total = 0
    for letra in texto.lower():
        if letra in "aeiou":
            total += 1
    return total

assert contar_vocales("Hola PYTHON") == 3
assert contar_vocales("AEIOU") == 5
assert contar_vocales("") == 0
assert contar_vocales("áéíóú") == 0
print("Pruebas superadas")

El acumulador nace dentro de la función y se reinicia en cada llamada. El return está fuera del bucle: así no abandonas el recorrido tras la primera letra.

Ejercicio 06

Calcula una media con un contrato explícito

Escribe media(numeros). Recibe una lista de números y devuelve su media aritmética. Si la lista está vacía, lanza ValueError con el mensaje "La lista está vacía".

Comprobaciones · añade tu función antes
assert media([2, 4, 6]) == 4
assert media([-2, 2]) == 0
assert media([7]) == 7
try:
    media([])
except ValueError as error:
    assert str(error) == "La lista está vacía"
else:
    raise AssertionError("Falta rechazar la lista vacía")
Ver una pista para el ejercicio 6

Comprueba la lista antes de dividir. Así una entrada vacía no termina en una división entre cero.

Ver solución y explicación del ejercicio 6
Solución completa · ejercicio 06
def media(numeros):
    if not numeros:
        raise ValueError("La lista está vacía")
    return sum(numeros) / len(numeros)

assert media([2, 4, 6]) == 4
assert media([-2, 2]) == 0
assert media([7]) == 7
try:
    media([])
except ValueError as error:
    assert str(error) == "La lista está vacía"
else:
    raise AssertionError("Falta rechazar la lista vacía")
print("Pruebas superadas")

Una media sin datos no está definida por este contrato. La prueba con try comprueba tanto la excepción como su mensaje; el bloque else falla si la función acepta la lista vacía.

Ejercicio 07

Quita duplicados conservando el orden

Escribe sin_duplicados(elementos). Recibe una lista de números o textos y devuelve una lista nueva con la primera aparición de cada valor. Conserva el orden y no modifica la entrada.

Comprobaciones · añade tu función antes
entrada = [3, 1, 3, 2, 1]
assert sin_duplicados(entrada) == [3, 1, 2]
assert entrada == [3, 1, 3, 2, 1]
assert sin_duplicados([]) == []
assert sin_duplicados(["a", "a", "b"]) == ["a", "b"]
Ver una pista para el ejercicio 7

Construye una lista de resultado. Añade un elemento solamente si todavía no está en esa lista.

Ver solución y explicación del ejercicio 7
Solución completa · ejercicio 07
def sin_duplicados(elementos):
    resultado = []
    for elemento in elementos:
        if elemento not in resultado:
            resultado.append(elemento)
    return resultado

entrada = [3, 1, 3, 2, 1]
assert sin_duplicados(entrada) == [3, 1, 2]
assert entrada == [3, 1, 3, 2, 1]
assert sin_duplicados([]) == []
assert sin_duplicados(["a", "a", "b"]) == ["a", "b"]
print("Pruebas superadas")

La lista nueva protege la entrada y conserva el orden. Esta solución prioriza la claridad; para listas grandes de valores hashables puedes combinar una lista con un set de elementos ya vistos.

Ejercicio 08

Cuenta palabras con un diccionario

Escribe contar_palabras(texto). Convierte el texto a minúsculas, sepáralo por espacios en blanco y devuelve las frecuencias. La puntuación se conserva: "hola" y "hola," son palabras distintas en este ejercicio.

Comprobaciones · añade tu función antes
assert contar_palabras("Sol luna sol") == {"sol": 2, "luna": 1}
assert contar_palabras("") == {}
assert contar_palabras("hola hola,") == {"hola": 1, "hola,": 1}
Ver una pista para el ejercicio 8

Para cada palabra, lee su contador con get(palabra, 0) y guarda el valor anterior más uno.

Ver solución y explicación del ejercicio 8
Solución completa · ejercicio 08
def contar_palabras(texto):
    frecuencias = {}
    for palabra in texto.lower().split():
        frecuencias[palabra] = frecuencias.get(palabra, 0) + 1
    return frecuencias

assert contar_palabras("Sol luna sol") == {"sol": 2, "luna": 1}
assert contar_palabras("") == {}
assert contar_palabras("hola hola,") == {"hola": 1, "hola,": 1}
print("Pruebas superadas")

split sin argumentos agrupa los espacios en blanco y no crea palabras vacías por espacios repetidos. El diccionario asocia cada palabra con su cantidad; el resultado de una entrada vacía es otro diccionario vacío.

Nivel 3

Validar y combinar

Ejercicio 09

Valida y calcula un descuento

Escribe aplicar_descuento(precio, porcentaje). Recibe números finitos, exige precio mayor o igual a cero y porcentaje entre cero y cien, y devuelve el resultado redondeado a dos decimales. Si un valor queda fuera de esos rangos, lanza ValueError.

Comprobaciones · añade tu función antes
assert aplicar_descuento(80, 25) == 60
assert aplicar_descuento(25, 100) == 0
assert aplicar_descuento(10, 0) == 10
for precio, porcentaje in [(-1, 10), (10, -1), (10, 101)]:
    try:
        aplicar_descuento(precio, porcentaje)
    except ValueError:
        pass
    else:
        raise AssertionError("Falta validar el rango")
Ver una pista para el ejercicio 9

Valida antes de calcular. El factor del precio es 1 − porcentaje / 100.

Ver solución y explicación del ejercicio 9
Solución completa · ejercicio 09
def aplicar_descuento(precio, porcentaje):
    if precio < 0 or not 0 <= porcentaje <= 100:
        raise ValueError("Precio o porcentaje fuera de rango")
    return round(precio * (1 - porcentaje / 100), 2)

assert aplicar_descuento(80, 25) == 60
assert aplicar_descuento(25, 100) == 0
assert aplicar_descuento(10, 0) == 10
for precio, porcentaje in [(-1, 10), (10, -1), (10, 101)]:
    try:
        aplicar_descuento(precio, porcentaje)
    except ValueError:
        pass
    else:
        raise AssertionError("Falta validar el rango")
print("Pruebas superadas")

La validación evita resultados matemáticamente calculables pero inválidos para el problema. Aquí usamos float para practicar funciones; para contabilidad con cantidades decimales exactas, consulta el proyecto que usa Decimal.

Ejercicio 10

Compón un resumen a partir de otra función

Escribe resumir(numeros). Devuelve un diccionario con cantidad, total y media. Reutiliza media del ejercicio 6. Para una lista vacía, devuelve cantidad cero, total cero y media None sin llamar a media([]).

Comprobaciones · añade tu función antes
assert resumir([2, 4, 6]) == {"cantidad": 3, "total": 12, "media": 4}
assert resumir([]) == {"cantidad": 0, "total": 0, "media": None}
assert resumir([5]) == {"cantidad": 1, "total": 5, "media": 5}
Ver una pista para el ejercicio 10

El caso vacío debe resolverse antes de invocar una función cuyo contrato rechaza precisamente esa entrada.

Ver solución y explicación del ejercicio 10
Solución completa · ejercicio 10
def media(numeros):
    if not numeros:
        raise ValueError("La lista está vacía")
    return sum(numeros) / len(numeros)

def resumir(numeros):
    promedio = media(numeros) if numeros else None
    return {
        "cantidad": len(numeros),
        "total": sum(numeros),
        "media": promedio,
    }

assert resumir([2, 4, 6]) == {"cantidad": 3, "total": 12, "media": 4}
assert resumir([]) == {"cantidad": 0, "total": 0, "media": None}
assert resumir([5]) == {"cantidad": 1, "total": 5, "media": 5}
print("Pruebas superadas")

Componer funciones exige respetar sus contratos. None diferencia un promedio ausente de un promedio igual a cero. El código incluye media para que puedas ejecutar la solución por separado.

Más ejercicios resueltos por tema

Cuando domines las funciones, practica con colecciones, texto y decisiones en estas guías.

Errores que estos ejercicios ayudan a detectar

La función devuelve None
Revisa si falta return o si lo has puesto en una rama que no se ejecuta. Repasa retornos.
Solo devuelve el primer elemento
Un return dentro de un bucle termina toda la función. Acumula el resultado y devuelve después del bucle.
El resultado cambia al llamar dos veces
Comprueba si modificas la lista recibida o dependes de una variable global. Prueba la misma entrada dos veces y compara también la entrada original.
Las pruebas normales pasan, pero una lista vacía falla
Decide el comportamiento antes de calcular: devolver una lista vacía, devolver None o lanzar una excepción son contratos diferentes.

Si no entiendes el traceback, empieza por la última línea en la guía de errores. Una prueba que falla es información sobre el problema que todavía debes resolver.

Preguntas frecuentes sobre ejercicios de funciones

¿Mi solución tiene que ser idéntica a la publicada?

No. Debe cumplir el contrato y los casos de prueba. Un bucle explícito puede ser más fácil de explicar que una expresión compacta; poder justificar tu solución importa más que escribir menos líneas.

¿Qué hago si todos los assert pasan?

Añade un caso que no estuviera en el enunciado, dentro de las entradas permitidas. Comprueba si la función conserva sus argumentos y si funciona en una segunda llamada.

¿Puedo ejecutar estas soluciones en el móvil?

Sí. El botón «Probar en el editor» lleva el ejemplo al editor del sitio. Necesitarás conexión para cargar el motor la primera vez; el teclado externo facilita escribir bloques largos.

Usa varias funciones en un programa

Cuando puedas explicar los diez contratos, elige un proyecto pequeño y separa sus cálculos de su entrada y salida.

Elegir mi primer proyecto →Seguir con ejercicios de fundamentos

Fuentes y compatibilidad

Los ejemplos usan Python 3 y la biblioteca estándar. Las instrucciones de instalación se contrastaron con la documentación oficial; los ejemplos de código se verifican con las comprobaciones de estas guías.

Consulta la metodología y las versiones del editor.