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.
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:
def saludar(nombre):
print("Hola,", nombre)
saludar("Ana")
saludar("Luis")python saludar.pyHola, Ana
Hola, LuisDesglosando la primera línea, def saludar(nombre):
defes la palabra clave que anuncia “aquí empieza una función” (viene de define).saludares 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,nombrese comporta como una variable normal.- Los dos puntos
:abren el bloque, y el cuerpo va indentado, exactamente como en los bloques deifyfor. 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).
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.
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:
double promedio(double a, double b) { // C++: tipos obligatorios
return (a + b) / 2.0;
}En Python nada de eso se escribe:
def promedio(a, b):
return (a + b) / 2Esto 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.
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.
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)Promedio: 4.5
Doble del promedio: 9.0La 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.
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:
def promedio_malo(a, b):
print((a + b) / 2) # imprime, pero no devuelve
total = promedio_malo(5.5, 3.5) * 2 # TypeErrorEse 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:
def saludar(nombre, saludo="Hola"):
print(saludo + ",", nombre)
saludar("Ana") # usa el saludo por defecto
saludar("Luis", "Buenas tardes") # lo reemplazaHola, Ana
Buenas tardes, LuisEsto 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:
saludar(nombre="Carla", saludo="Buenos días")
saludar(saludo="Qué tal", nombre="Marta") # el orden ya no importaLos 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").
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:
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:
python clasificar.py6.5 -> excelente
3.0 -> reprobado
4.5 -> aprobado
8.0 -> fuera de rango
Promedio del curso: 5.5Lo que este ejemplo demuestra:
clasificar()usa variosreturnen distintas ramas. Cada uno termina la función al instante, así que no hace falta ningúnelsefinal ni ninguna variable temporal para arrastrar el resultado.promedio()recorre una lista con el mismoforde la página anterior y acumula entotalcon 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 deifsería insostenible; escribirla una vez dentro de una función y llamarla cuatro veces es el objetivo entero de esta página.
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.
def calcular():
resultado = 42 # local: solo existe aquí dentro
return resultado
print(calcular())
# print(resultado) # NameError: name 'resultado' is not definedEsto 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.
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.