Módulo 1 · Lección 34 de 59
El valor de retorno
Por qué imprimir y devolver no son lo mismo, cómo funciona return y qué pasa cuando una función no lo tiene.
Las funciones que has escrito hasta ahora imprimen cosas. Está bien para una cabecera o un menú, pero la mayoría de las funciones útiles no sirven para enseñar algo en pantalla, sino para calcular un resultado que el resto del programa va a usar.
Esa diferencia, entre mostrar un valor y entregarlo, es el malentendido número uno de quien empieza con funciones. Si lo dejas claro hoy, te ahorras semanas de confusión.
Imprimir no es devolver#
Esta función parece que calcula el IVA:
def mostrar_iva(precio):
print(precio * 0.21)
mostrar_iva(100)21.0Sale el 21. Parece que funciona. Pero prueba a guardar ese resultado en una variable:
total = mostrar_iva(100)
print(total)21.0
NoneEl 21 lo imprime la función. Después imprimimos total y sale None, que es la forma que tiene Python de decir "nada". La función ha escrito el número en la pantalla y lo ha tirado. No te lo ha dado.
Y en cuanto intentas usarlo, el programa se rompe:
iva = mostrar_iva(100)
print(iva + 100)21.0
Traceback (most recent call last):
File "/Users/erwin/curso-python/factura.py", line 6, in <module>
print(iva + 100)
~~~~^~~~~
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'return#
Cambiar print por return lo arregla todo:
def calcular_iva(precio):
return precio * 0.21
iva = calcular_iva(100)
print(iva)
print(100 + calcular_iva(100))
print(f"IVA de la cesta: {calcular_iva(37.5):.2f} euros")21.0
121.0
IVA de la cesta: 7.88 eurosAhora la llamada calcular_iva(100) es un valor. Se comporta igual que si hubieras escrito 21.0 en ese sitio: puedes guardarlo, sumarlo, meterlo en una f-string, pasarlo a otra función o ponerlo en una lista.
Fíjate también en el nombre. mostrar_iva empieza por un verbo de mostrar; calcular_iva, por uno de calcular. El nombre ya te dice cuál de las dos devuelve algo.
return termina la función inmediatamente#
En cuanto Python ejecuta un return, sale de la función. Lo que haya debajo no se ejecuta:
def calcular_iva(precio):
print("Calculando el IVA...")
return precio * 0.21
print("Esta línea no se ejecuta nunca")
print(calcular_iva(100))Calculando el IVA...
21.0El último print está ahí, escrito y bien sangrado, y nunca se ejecutará. Python no da ningún error por ello; simplemente es código muerto. Algunos editores te lo marcan en gris.
Esto no significa que solo pueda haber un return por función. Puede haber varios, en ramas distintas de un if; lo que ocurre es que el primero que se ejecute acaba la función.
def clasificar_nota(nota):
if nota < 5:
return "Suspenso"
if nota < 7:
return "Aprobado"
if nota < 9:
return "Notable"
return "Sobresaliente"
print(clasificar_nota(4))
print(clasificar_nota(8.5))Suspenso
NotableUna función sin return devuelve None#
Toda función devuelve algo, siempre. Si no escribes return, Python devuelve None por ti:
def guardar_nota(nota):
print(f"Nota guardada: {nota}")
resultado = guardar_nota(8)
print(resultado)
print(type(resultado))Nota guardada: 8
None
<class 'NoneType'>None es un valor de pleno derecho, con su propio tipo, NoneType. Significa "ausencia de valor" y lo verás por todas partes en Python.
Hay un caso especialmente traicionero: la función que a veces devuelve algo.
def es_mayor_de_edad(edad):
if edad >= 18:
return True
print(es_mayor_de_edad(20))
print(es_mayor_de_edad(15))True
NoneCuando la condición no se cumple, la función llega al final sin encontrar ningún return y devuelve None. En un if eso se comporta como falso y parece que funciona, pero None no es False, y el día que compares con == False o metas el resultado en una lista te llevarás una sorpresa. Escribe siempre el return de todos los caminos:
def es_mayor_de_edad(edad):
return edad >= 18Devolver varios valores a la vez#
Pon varios valores separados por comas después del return y Python los empaqueta en una tupla:
def analizar_notas(notas):
media = sum(notas) / len(notas)
return media, min(notas), max(notas)
resultado = analizar_notas([7, 9, 5, 8])
print(resultado)(7.25, 5, 9)No hay nada nuevo aquí: es el mismo empaquetado de tuplas que viste en su lección, y por eso lo normal es recogerlo desempaquetando en varias variables de golpe:
media, peor, mejor = analizar_notas([7, 9, 5, 8])
print(f"Media: {media:.2f}")
print(f"Peor nota: {peor}")
print(f"Mejor nota: {mejor}")Media: 7.25
Peor nota: 5
Mejor nota: 9Es cómodo, pero con moderación. Si tu función devuelve cinco o seis valores, quien la llama tiene que recordar el orden exacto de todos, y eso se equivoca solo. A partir de tres valores, plantéate devolver un diccionario:
def analizar_notas(notas):
return {
"media": sum(notas) / len(notas),
"peor": min(notas),
"mejor": max(notas),
"aprobadas": len([nota for nota in notas if nota >= 5]),
}
resumen = analizar_notas([7, 9, 5, 8])
print(resumen["media"])
print(resumen["aprobadas"])7.25
4Con el diccionario, quien lo recibe pide lo que necesita por su nombre y no depende de ningún orden.
El retorno temprano#
Como return corta la función en seco, se puede usar para quitarse de encima los casos raros al principio y dejar el caso normal sin anidar. Compara estas dos versiones de lo mismo:
def calcular_descuento(importe, socio):
if importe > 0:
if socio:
if importe >= 100:
return importe * 0.20
else:
return importe * 0.10
else:
return 0.0
else:
return 0.0def calcular_descuento(importe, socio):
if importe <= 0:
return 0.0
if not socio:
return 0.0
if importe >= 100:
return importe * 0.20
return importe * 0.10Las dos dan exactamente el mismo resultado:
print(calcular_descuento(150, True))
print(calcular_descuento(40, True))
print(calcular_descuento(150, False))30.0
4.0
0.0La segunda versión se lee de arriba abajo como una lista de reglas, nunca pasa de un nivel de sangría y no te obliga a sostener mentalmente tres if abiertos para saber en qué caso estás. Es el patrón llamado retorno temprano o early return, y cuando te acostumbras a él ya no vuelves atrás.