Funciones en Python
En esta lección
- Definir y llamar a una función
- Parámetros y argumentos
- return: devolver un resultado
- Argumentos posicionales y con nombre
- Valores por defecto
- Devolver varios valores
- Ámbito: variables dentro y fuera
- Documenta tus funciones: docstrings
- Type hints: indicar los tipos
- Cómo diseñar buenas funciones
- Funciones lambda
- Errores frecuentes
- Resumen
Una función agrupa código con un nombre para reutilizarlo. En lugar de copiar las mismas cinco líneas en diez sitios, las escribes una vez y las llamas cuando las necesites. Si mañana hay que cambiarlas, las cambias en un único lugar. En esta lección aprenderás a definir funciones, pasarles datos, devolver resultados, documentarlas y, sobre todo, a diseñarlas bien.
Definir y llamar a una función
Las funciones se definen con def:
def saludar(nombre):
print(f"¡Hola, {nombre}!")
saludar("Ana")
saludar("Luis")
# ¡Hola, Ana!
# ¡Hola, Luis!
def, el nombre, los parámetros entre paréntesis y dos puntos.- El cuerpo va con sangría.
- Los nombres de funciones van en snake_case y suelen ser verbos:
calcular_total,enviar_correo,es_par.
Definir una función no la ejecuta: solo le da un nombre al código. Se ejecuta cada vez que la llamas escribiendo su nombre con paréntesis. Por eso la definición tiene que aparecer antes de la primera llamada; si no, obtendrás NameError.
Una función puede no tener parámetros, pero los paréntesis son obligatorios:
def mostrar_menu():
print("1) Nueva partida")
print("2) Salir")
mostrar_menu()
# 1) Nueva partida
# 2) Salir
Parámetros y argumentos
Dos palabras que se confunden a menudo:
- Los parámetros son los nombres que escribes en la definición:
nombreendef saludar(nombre). - Los argumentos son los valores concretos que pasas al llamarla:
"Ana"ensaludar("Ana").
Al llamar a la función, cada parámetro toma el valor de su argumento, como si hicieras nombre = "Ana" al principio del cuerpo. Si pasas menos o más argumentos de los que espera, obtienes un error:
saludar()
# TypeError: saludar() missing 1 required positional argument: 'nombre'
return: devolver un resultado
Para que la función devuelva un resultado en lugar de mostrarlo, usa return:
def area_rectangulo(base, altura):
return base * altura
area = area_rectangulo(5, 3)
print(area * 2)
# 30
¿Por qué devolver en lugar de imprimir? Porque un valor devuelto se puede reutilizar: guardarlo, operar con él, pasarlo a otra función o mostrarlo con el formato que quieras. Un print dentro de la función solo sirve para mirar.
return termina la función en ese momento. Esto permite salir pronto:
def clasificar_nota(nota):
if nota < 0 or nota > 10:
return "Nota no válida"
if nota >= 5:
return "Aprobado"
return "Suspenso"
print(clasificar_nota(7))
print(clasificar_nota(12))
# Aprobado
# Nota no válida
Una función sin return (o con return solo) devuelve None:
def doble(n):
n * 2 # calcula, pero no devuelve nada
print(doble(4))
# None
Es un error muy común: si la función debe dar un resultado, comprueba que lleva return.
Argumentos posicionales y con nombre
Al llamar a una función puedes pasar los argumentos de dos formas:
- Posicionales: por orden. El primero va al primer parámetro, el segundo al segundo…
- Con nombre (keyword arguments): indicando a qué parámetro va cada valor. Así el orden da igual y el código se lee mejor.
def crear_usuario(nombre, edad, ciudad):
return f"{nombre} ({edad}) - {ciudad}"
print(crear_usuario("Eva", 30, "Santander")) # posicionales
print(crear_usuario(ciudad="Santander", nombre="Eva", edad=30)) # con nombre
print(crear_usuario("Eva", ciudad="Santander", edad=30)) # mezcla
# Eva (30) - Santander
# Eva (30) - Santander
# Eva (30) - Santander
Al mezclar, los posicionales van siempre primero. crear_usuario(nombre="Eva", 30, "Santander") da SyntaxError.
Los argumentos con nombre son especialmente útiles cuando hay valores sueltos que no se entienden solos: enviar(correo, True, False) no dice nada; enviar(correo, urgente=True, copia=False) sí.
Valores por defecto
Un parámetro puede tener un valor que se usa si no se pasa el argumento:
def precio_final(precio, iva=0.21):
return precio * (1 + iva)
print(precio_final(100)) # 121.0
print(precio_final(100, 0.10)) # 110.00000000000001
print(precio_final(100, iva=0)) # 100
(El 110.00000000000001 es el efecto de la coma flotante que se explica en Operadores; al mostrarlo usarías f"{valor:.2f}".)
Los parámetros con valor por defecto van siempre después de los que no lo tienen: def f(iva=0.21, precio) da SyntaxError.
Cuidado: no uses una lista o un diccionario como valor por defecto (
def agregar(item, lista=[])). Ese objeto se crea una sola vez, al definir la función, y se comparte entre todas las llamadas. UsaNoney créalo dentro:def agregar(item, lista=None): if lista is None: lista = [] lista.append(item) return lista
Devolver varios valores
def estadisticas(numeros):
return min(numeros), max(numeros), sum(numeros) / len(numeros)
minimo, maximo, media = estadisticas([4, 8, 6, 10])
print(minimo, maximo, media)
# 4 10 7.0
En realidad devuelve una tupla, que se desempaqueta entre las tres variables. Lo tienes explicado en Tuplas y conjuntos. Si devuelves muchos valores (cuatro o más), plantéate devolver un diccionario: resultado["media"] se entiende mejor que resultado[2].
Ámbito: variables dentro y fuera
Las variables creadas dentro de una función solo existen dentro (son locales):
def calcular():
resultado = 42
calcular()
print(resultado)
# NameError: name 'resultado' is not defined
Cada llamada tiene sus propias variables locales, que desaparecen al terminar. La forma correcta de sacar un valor de una función es con return, no con variables globales. El ámbito, global y nonlocal se explican a fondo en *args, **kwargs, lambda y ámbito.
Documenta tus funciones: docstrings
La primera línea del cuerpo puede ser un texto entre triples comillas que explica qué hace la función. Se llama docstring y los editores lo muestran al pasar el ratón sobre la función:
def es_par(numero):
"""Devuelve True si el número es par y False si es impar."""
return numero % 2 == 0
Para funciones más complejas, el docstring puede ocupar varias líneas y describir los parámetros y lo que devuelve:
def calcular_descuento(precio, porcentaje):
"""Calcula el precio tras aplicar un descuento.
precio: precio original en euros.
porcentaje: descuento entre 0 y 100.
Devuelve el precio final redondeado a 2 decimales.
"""
return round(precio * (1 - porcentaje / 100), 2)
print(calcular_descuento(80, 15))
# 68.0
Puedes leer el docstring de cualquier función con help(calcular_descuento) o calcular_descuento.__doc__.
Type hints: indicar los tipos
Python no obliga a declarar tipos, pero puedes anotarlos para que quede claro qué espera y qué devuelve la función:
def calcular_media(notas: list[float]) -> float:
return sum(notas) / len(notas)
def saludar(nombre: str, veces: int = 1) -> None:
for _ in range(veces):
print(f"Hola, {nombre}")
print(calcular_media([7.5, 8, 6]))
saludar("Eva", veces=2)
# 7.166666666666667
# Hola, Eva
# Hola, Eva
: tipotras cada parámetro y-> tipoantes de los dos puntos para el valor devuelto.-> Noneindica que la función no devuelve nada.
Las anotaciones no se comprueban al ejecutar: calcular_media("hola") no daría error por el tipo, sino por lo que pase dentro. Entonces, ¿para qué sirven? Documentan el código y permiten que el editor (VS Code, PyCharm) o herramientas como mypy te avisen de errores antes de ejecutar.
Cómo diseñar buenas funciones
Escribir funciones es fácil; escribir funciones buenas requiere algunas reglas:
- Una sola tarea. Si para describirla necesitas la palabra “y” (“lee el fichero y calcula la media y la imprime”), probablemente son tres funciones.
- Nombre descriptivo.
calcular_iva(precio)se entiende sin leer el código;proc(p)no. - Recibe los datos por parámetros y devuelve el resultado. Evita que dependa de variables globales o que imprima en lugar de devolver: así podrás reutilizarla y probarla.
- Pocas líneas y pocos parámetros. Si pasa de unas 20-30 líneas o de 4-5 parámetros, plantéate dividirla.
- Sin efectos sorpresa. Si una función se llama
calcular_total, no debería también vaciar el carrito.
Compara estas dos versiones:
# Mal: mezcla cálculo y presentación, y depende de una global
carrito = [12.5, 3.2, 40]
def total():
suma = 0
for p in carrito:
suma += p
print("Total:", suma)
# Bien: recibe los datos, calcula y devuelve
def calcular_total(precios: list[float]) -> float:
"""Suma los precios de una lista."""
return sum(precios)
print(f"Total: {calcular_total(carrito):.2f} €")
# Total: 55.70 €
La segunda sirve para cualquier lista, se puede usar dentro de otros cálculos y muestra el resultado con el formato que prefiera quien la llama.
Funciones lambda
Python también permite funciones muy cortas y sin nombre, las lambda, que se usan sobre todo como argumento de sorted, max o min: sorted(alumnos, key=lambda a: a["nota"]). Las verás, junto con *args y **kwargs, en Funciones avanzadas.
Errores frecuentes
- Olvidar el
return: la función devuelveNoney el resto del programa falla después. - Llamar sin paréntesis:
mostrar_menu(sin()) no ejecuta nada; solo nombra la función. - Número incorrecto de argumentos:
TypeError: ... missing 1 required positional argument. - Posicionales después de con nombre:
f(a=1, 2)esSyntaxError. - Parámetro por defecto antes de uno obligatorio:
def f(a=1, b)esSyntaxError. - Lista como valor por defecto: se comparte entre llamadas. Usa
None. - Usar dentro variables creadas en otra función:
NameError. Pásalas como argumento.
Resumen
| Concepto | Ejemplo |
|---|---|
| Definir | def calcular_total(precios): |
| Llamar | calcular_total([1, 2]) |
| Devolver | return resultado (sin return → None) |
| Valor por defecto | def precio_final(precio, iva=0.21): |
| Argumento con nombre | precio_final(100, iva=0.10) |
| Varios valores | return a, b y x, y = f() |
| Docstring | """Qué hace la función.""" |
| Type hints | def media(notas: list[float]) -> float: |
| Ámbito | Las variables locales solo existen dentro |
Sigue con Funciones avanzadas para aprender *args, **kwargs, lambda y el ámbito de las variables.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.