</>waridocu

Python

Funciones: def, parámetros y return

Cómo definir funciones en Python con def, pasarles parámetros con valores por defecto y devolver resultados con return.

pythonfuncionesdefreturnparametros

De dónde venimos

En Control de flujo: if, for y while escribiste un menú que funcionaba, pero que tenía un problema de forma: todo el código vivía en un único archivo que se lee de arriba a abajo. Si ese programa creciera —validar la opción, calcular algo, imprimir un informe— acabaría siendo un bloque de doscientas líneas imposible de seguir, con trozos de lógica repetidos en varios sitios.

Las funciones son la herramienta para partir ese bloque en piezas con nombre, cada una responsable de una sola cosa. Esta página no introduce ninguna lógica nueva: por dentro, una función contiene exactamente los mismos if, for y while que ya sabes escribir. Lo nuevo es la envoltura que les pone nombre y les permite recibir datos y devolver resultados.

Qué es una función

Una función es un bloque de código con nombre que puedes ejecutar cuantas veces quieras, pasándole datos distintos cada vez. Ya has usado varias sin darte cuenta: print(), input(), int() y float(), len() y range() son funciones que vienen incluidas en Python. Ahora vas a escribir las tuyas.

La sintaxis para definir una es:

Ejemplo: definir y llamar una funciónpython
def saludar(nombre):
    print("Hola,", nombre)

saludar("Ana")
saludar("Luis")
bashbash
python saludar.py
texttext
Hola, Ana
Hola, Luis

Desglosando la primera línea, def saludar(nombre):

  • def es la palabra clave que anuncia “aquí empieza una función” (viene de define).
  • saludar es el nombre que le das. Se escribe en minúsculas con guiones bajos, igual que las variables.
  • (nombre) es la lista de parámetros: los datos que la función espera recibir. Dentro del cuerpo, nombre se comporta como una variable normal.
  • Los dos puntos : abren el bloque, y el cuerpo va indentado, exactamente como en los bloques de if y for. Aquí no hay ninguna regla nueva de indentación.

Definir la función no ejecuta nada. El cuerpo solo corre cuando la llamas, escribiendo su nombre seguido de paréntesis con los valores concretos: saludar("Ana"). A esos valores que pasas al llamar se les llama argumentos, para distinguirlos de los parámetros (los nombres que aparecen en la definición).

Advertencia

Escribir el nombre de la función sin los paréntesis no la llama. saludar a secas es una expresión válida que se refiere a la función como objeto, no la ejecuta: el programa no da error y no imprime nada. Si tu función “no hace nada”, lo primero que debes revisar es si le faltan los paréntesis.

Importante

Python lee el archivo de arriba a abajo, así que una función debe estar definida antes de la línea que la llama. Si pones saludar("Ana") en la primera línea y el def debajo, obtienes NameError: name 'saludar' is not defined. La convención habitual es agrupar todas las definiciones arriba del archivo y dejar el código que las usa al final.

Sin tipos: la diferencia grande con C++

En C++, una función obliga a declarar el tipo de cada parámetro y el tipo de lo que devuelve:

cppcpp
double promedio(double a, double b) {   // C++: tipos obligatorios
    return (a + b) / 2.0;
}

En Python nada de eso se escribe:

Ejemplo: la misma función, sin declarar tipospython
def promedio(a, b):
    return (a + b) / 2

Esto es coherente con el tipado dinámico que viste desde los tipos básicos: igual que una variable no declara su tipo, un parámetro tampoco. La función acepta lo que le pases y solo falla si la operación de dentro no tiene sentido para ese tipo.

La consecuencia tiene dos caras. La buena: la misma función sirve para int y para float sin escribirla dos veces. La mala: nadie te avisa antes de ejecutar si le pasas algo incorrecto. promedio("hola", "adiós") no da error al escribirlo; revienta en tiempo de ejecución, cuando el programa ya está corriendo delante del usuario.

Nota

Python permite escribir los tipos de forma opcional, como anotación informativa: def promedio(a: float, b: float) -> float:. Esas anotaciones no cambian nada en tiempo de ejecución (Python no las verifica ni las obliga), pero sirven para documentar y para que herramientas externas revisen el código antes de ejecutarlo. Es muy común en proyectos grandes. Aquí no las usaremos para no mezclar dos sintaxis mientras aprendes la base.

Devolver un resultado con return

return termina la función inmediatamente y entrega un valor a quien la llamó. Ese valor se puede guardar en una variable o usar directamente en una expresión.

Ejemplo: una función que calcula y devuelve un valorpython
def promedio(a, b):
    return (a + b) / 2

resultado = promedio(5.5, 3.5)
print("Promedio:", resultado)
print("Doble del promedio:", promedio(5.5, 3.5) * 2)
texttext
Promedio: 4.5
Doble del promedio: 9.0

La diferencia entre print() y return confunde a casi todo el mundo al principio, así que conviene dejarla clara: print() muestra algo en la pantalla y no devuelve nada aprovechable; return entrega un valor al resto del programa, que puede seguir operando con él. Una función que calcula debería devolver, no imprimir: así quien la llama decide si imprimir el resultado, guardarlo o sumarlo a otra cosa.

Advertencia

Una función sin return sí devuelve algo: devuelve None, el valor especial de Python para “nada”. El error típico es escribir una función que imprime en vez de devolver y luego intentar usar su resultado:

pythonpython
def promedio_malo(a, b):
    print((a + b) / 2)      # imprime, pero no devuelve

total = promedio_malo(5.5, 3.5) * 2   # TypeError

Ese código imprime 4.5 y acto seguido falla con TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'. Cuando veas NoneType en un mensaje de error, sospecha de un return que falta.

return también corta la ejecución en el acto: cualquier línea que quede después de un return que se ejecute no llega a correr nunca. Eso se aprovecha para salir temprano de la función.

Parámetros con valor por defecto

Un parámetro puede tener un valor predefinido, que se usa solo si quien llama no proporciona ese argumento:

Ejemplo: parámetro con valor por defectopython
def saludar(nombre, saludo="Hola"):
    print(saludo + ",", nombre)

saludar("Ana")                      # usa el saludo por defecto
saludar("Luis", "Buenas tardes")    # lo reemplaza
texttext
Hola, Ana
Buenas tardes, Luis

Esto evita tener que escribir dos funciones casi idénticas, o forzar a quien llama a repetir siempre el mismo valor.

También puedes pasar los argumentos por nombre en vez de por posición, lo que hace la llamada mucho más legible cuando hay varios parámetros:

Ejemplo: argumentos por nombrepython
saludar(nombre="Carla", saludo="Buenos días")
saludar(saludo="Qué tal", nombre="Marta")   # el orden ya no importa
Advertencia

Los parámetros con valor por defecto tienen que ir después de los obligatorios. def saludar(saludo="Hola", nombre): no es válido y produce SyntaxError: parameter without a default follows parameter with a default. La razón es lógica: si el que tiene defecto fuera primero, Python no podría saber a cuál corresponde el único argumento de una llamada como saludar("Ana").

Advertencia

Nunca uses una lista (ni ningún otro valor modificable) como valor por defecto: def agregar(item, lista=[]):. Esa lista se crea una sola vez, al definir la función, y se comparte entre todas las llamadas, así que los elementos se van acumulando de una llamada a la siguiente en vez de empezar vacía cada vez. Es una de las trampas más famosas del lenguaje. La solución habitual es usar None como defecto y crear la lista dentro del cuerpo. Tendrá mucho más sentido cuando veas las listas a fondo; por ahora basta con que la señal de alarma te suene.

Ejemplo completo: una función con lógica dentro

Una función solo cobra sentido cuando encierra algo que valía la pena encapsular. Este ejemplo mete dentro de funciones exactamente el if/elif/else y el for de la página anterior:

Ejemplo: clasificar varias notas usando funcionespython
def clasificar(nota):
    if nota < 1.0 or nota > 7.0:
        return "fuera de rango"
    elif nota >= 6.0:
        return "excelente"
    elif nota >= 4.0:
        return "aprobado"
    else:
        return "reprobado"


def promedio(valores):
    total = 0
    for valor in valores:
        total += valor
    return total / len(valores)


notas = [6.5, 3.0, 4.5, 8.0]

for nota in notas:
    print(nota, "->", clasificar(nota))

print("Promedio del curso:", promedio(notas))

Guárdalo como clasificar.py y ejecútalo:

bashbash
python clasificar.py
texttext
6.5 -> excelente
3.0 -> reprobado
4.5 -> aprobado
8.0 -> fuera de rango
Promedio del curso: 5.5

Lo que este ejemplo demuestra:

  • clasificar() usa varios return en distintas ramas. Cada uno termina la función al instante, así que no hace falta ningún else final ni ninguna variable temporal para arrastrar el resultado.
  • promedio() recorre una lista con el mismo for de la página anterior y acumula en total con el operador +=. Recibe la lista entera como un único parámetro: una función puede recibir cualquier tipo de dato, no solo números o texto.
  • El bucle final llama a clasificar() una vez por nota. Escribir cuatro veces la cadena de if sería insostenible; escribirla una vez dentro de una función y llamarla cuatro veces es el objetivo entero de esta página.
Tip

Las líneas en blanco entre funciones no son decorativas: la guía de estilo oficial de Python (PEP 8) recomienda dos líneas en blanco entre definiciones de funciones a nivel del archivo. Ayuda a distinguir de un vistazo dónde termina una y empieza otra, algo especialmente valioso en un lenguaje donde no hay llaves que cierren los bloques.

Variables dentro y fuera de una función

Una variable creada dentro de una función es local: existe solo mientras la función se ejecuta y desaparece al terminar.

Ejemplo: una variable local no sobrevive a la funciónpython
def calcular():
    resultado = 42      # local: solo existe aquí dentro
    return resultado

print(calcular())
# print(resultado)      # NameError: name 'resultado' is not defined

Esto es deseable, no una limitación: garantiza que una función no pise por accidente las variables de otra, y es lo que permite que cada pieza se pueda entender y probar por separado.

Nota

Al revés sí funciona en parte: una función puede leer una variable definida fuera de ella. Pero apoyarse en eso es mala práctica, porque vuelve la función dependiente de un contexto invisible desde su definición. La regla sana: todo lo que la función necesite, que entre por sus parámetros; todo lo que produzca, que salga por su return.

Siguiente paso

Con variables, control de flujo y funciones ya tienes la base completa del lenguaje: cualquier programa de Python, por grande que sea, está hecho de estas tres piezas combinadas.

Lo que viene a continuación es aprender a manejar datos en cantidad, y ahí quedan tres temas pendientes que ya se asomaron de refilón en estas páginas:

  • Listas a fondo — las viste recorrer con un for, pero falta acceder por posición, agregar y quitar elementos, ordenarlas y recortarlas.
  • Diccionarios — la estructura para guardar pares de clave y valor ("nombre": "Ana"), cuando el orden importa menos que poder buscar por un identificador.
  • Clases y objetos — la forma de agrupar datos y las funciones que operan sobre ellos en un mismo molde reutilizable.

Esas páginas aún no están escritas. Mientras tanto, la mejor forma de consolidar lo visto es reescribir el menú interactivo de la página anterior partiendo cada opción en su propia función: es el ejercicio que junta todo lo de esta sección.