Pregunta 1¿Qué imprime este código?def greet():
print("Hi")
f = greet
f()
Funciones de orden superior — Tratar funciones como argumentos y valores de retorno
Aprende las funciones de orden superior de Python: trata las funciones como argumentos y valores de retorno.
En el artículo anterior sobre yield viste funciones que producen valores uno a uno. Esta vez seguirás del lado de las funciones y mirarás las funciones de orden superior — el mecanismo que te permite tratar una función misma como un valor.
Tres patrones para tener presentes: asignarla a una variable, pasarla como argumento (un callback) y retornarla como valor.
Qué es una función de orden superior — Las funciones también son valores
En Python, las funciones son valores igual que los enteros o las cadenas. Puedes meterlas en variables, pasarlas a otras funciones como argumentos y retornarlas como resultado.
Una función que toma una función como argumento, o que retorna una función como resultado, se llama función de orden superior. A diferencia de print() o len(), que solo operan sobre valores, las funciones de orden superior ensamblan piezas de comportamiento.
Asignar una función a una variable — Las funciones también son valores referenciados
Cuando defines una función con def, el cuerpo de la función se construye en memoria y el nombre de la función apunta a esa ubicación. Asigna con a = print y a ahora apunta al mismo cuerpo de función, así que a("HOLA") y print("HOLA") hacen exactamente lo mismo.
El truco es dejar fuera los () después del nombre de la función. Añade () y en su lugar llamas a la función, y el valor de retorno queda asignado.
a = print # sin () — asigna el cuerpo de la función a a
a("HOLA") # HOLA ← lo mismo que print("HOLA")
print(id(print)) # p. ej. 4395020128
print(id(a)) # aparece la misma dirección
# Tus propias funciones funcionan igual
def greet():
print("Hola")
say = greet # construye un alias say
say() # Hola
Con o sin () cambia el significado
a = print es la forma que pasa la función, mientras que a = print("HOLA") es la forma que llama a la función y pasa el resultado. En el segundo caso, el valor de retorno de print("HOLA") (None) cae en a, así que llamar a a() lanza TypeError: 'NoneType' object is not callable.
Pasar una función como argumento — Callbacks
El uso más típico de las funciones de orden superior es el callback — «una función que se pasa a otra función y se llama en un momento concreto», donde «el que llama decide el comportamiento real».
Por ejemplo, supón que tienes una rutina que «imprime un saludo para una lista de tres nombres», pero quieres intercambiar solo el estilo del saludo desde el punto de llamada. Mantén el cuerpo como un bucle que extrae los nombres uno a uno, y toma la pieza de formateo como argumento — entonces el que llama puede reutilizar la rutina con solo cambiar la plantilla del saludo.
def greet_all(names, formatter):
for name in names:
print(formatter(name))
def formal(name):
return f"Estimado/a {name}, gracias por tu fidelidad."
def casual(name):
return f"¡Hola, {name}!"
greet_all(["Ana", "Carlos", "Marta"], formal)
# Estimado/a Ana, gracias por tu fidelidad.
# Estimado/a Carlos, gracias por tu fidelidad.
# Estimado/a Marta, gracias por tu fidelidad.
greet_all(["Ana", "Carlos", "Marta"], casual)
# ¡Hola, Ana!
# ¡Hola, Carlos!
# ¡Hola, Marta!
greet_all es solo un bucle; cómo convertir cada nombre en una línea de saludo se deja al argumento formatter.Ramificar con callbacks específicos por propósito
No estás limitado a pasar un único callback. «Usa esta función en caso de éxito, esa otra en caso de fallo» es otro encaje natural — en cualquier sitio donde quieras elegir entre varios callbacks.
Por ejemplo, cuando quieres alternar el comportamiento según un número sea par o impar, la función que decide puede no hacer más que una ramificación con if, y dejar el procesamiento real a dos funciones proporcionadas por el que llama.
def process_number(number, even_callback, odd_callback):
if number % 2 == 0:
even_callback(number)
else:
odd_callback(number)
def handle_even(n):
print(f"{n} es par")
def handle_odd(n):
print(f"{n} es impar")
process_number(4, handle_even, handle_odd) # 4 es par
process_number(7, handle_even, handle_odd) # 7 es impar
Retornar una función como valor — Clausuras en la práctica
El otro patrón de orden superior es «retornar una función como valor». Retornar una función interna con return se cubrió en el artículo sobre clausuras; aquí, el foco está en el ángulo práctico de «entregar al que llama una función que ya recuerda su configuración».
Por ejemplo, cuando quieres producir en serie funciones de log que estampan el mismo prefijo en cada llamada, prepara make_logger("INFO") para el logger INFO y make_logger("ERROR") para el logger ERROR — y el código que las llama queda tan corto como info("Proceso iniciado").
def make_logger(prefix):
def log(message):
print(f"[{prefix}] {message}")
return log # retorna la función misma
info = make_logger("INFO")
error = make_logger("ERROR")
info("Proceso iniciado") # [INFO] Proceso iniciado
error("Conexión fallida") # [ERROR] Conexión fallida
info("Proceso finalizado") # [INFO] Proceso finalizado
- info = make_logger("INFO") — recibe una función que recuerda INFO
- error = make_logger("ERROR") — recibe una función separada que recuerda ERROR
- Mantiene
prefix = "INFO" returna ellogdefinido dentro
- Imprime
[INFO] ...en cada llamada
- Mantiene
prefix = "ERROR" returna una funciónlogseparada
- Un objeto función separado, independiente de
info
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2¿Cuál de estas líneas pasa say_hi como callback?
Pregunta 3¿Qué imprime info("OK") después de ejecutar este código?def make_logger(prefix):
def log(message):
print(f"[{prefix}] {message}")
return log
info = make_logger("INFO")