En Python, al pasar argumentos a funciones, los parámetros de diccionario pueden escribirse de dos maneras:
def funcion(**datos):
print(datos)
# Primera forma: asignación clave=valor
funcion(nombre="alex")
# Segunda forma: desempaquetar un diccionario con **
funcion(**{"nombre":"alex"})
Paso de listas con *args
def contar(*elementos):
print(elementos, "Cantidad de elementos:", len(elementos))
lista = [1, 2, 3, 4]
contar(lista) # Salida: ([1, 2, 3, 4],) Cantidad de elementos: 1
contar(*lista) # Salida: (1, 2, 3, 4) Cantidad de elementos: 4
Al llamar contar(lista) se pasa la lista completa como un único argumento. Con contar(*lista) se desempaqueta la lista y sus elementos se convierten en argumentos separados.
Ejemplo de decorador simple
def extraer_numeros(funcion):
def envoltura(*argumentos):
import re
resultado_original = funcion(*argumentos)
resultado = []
for arg in argumentos:
resultado.append(re.findall(r'\d+', arg))
return resultado
return envoltura
@extraer_numeros
def mostrar(*argumentos):
print(*argumentos)
datos = ["a88d66e3a", "wwa665wwa"]
print(mostrar(*datos))
# Salida:
# a88d66e3a wwa665wwa
# [['88', '66', '3'], ['665']]
Múltiples decoradores: orden de aplicación y ejecución
def deco1(funcion):
def interna1(*args, **kwargs):
print("Inicio deco1 >> interna1")
res1 = funcion(*args, **kwargs)
return res1
return interna1
def deco2(funcion):
def interna2(*args, **kwargs):
print("Inicio deco2 >> interna2")
res2 = funcion(*args, **kwargs)
return res2
return interna2
def deco3(parametro):
def externa3(funcion):
print("Parámetro de deco3:", parametro)
def interna3(*args, **kwargs):
print("Inicio deco3 >> externa3 >> interna3")
res3 = funcion(*args, **kwargs)
return res3
return interna3
return externa3
# Orden de aplicación: de abajo arriba: deco3(5) -> deco2 -> deco1
# Orden de ejecución: de arriba abajo: deco1 -> deco2 -> deco3
@deco1
@deco2
@deco3(5)
def indice(x, y):
print("Función indice ejecutándose con", x, "y", y)
indice("A", "B")
# Salida:
# Parámetro de deco3: 5
# Inicio deco1 >> interna1
# Inicio deco2 >> interna2
# Inicio deco3 >> externa3 >> interna3
# Función indice ejecutándose con A y B
Decorador para reintentos en peticiones HTTP
import requests
def con_reintentos(max_intentos):
def decorador(funcion):
def interno(*args, **kwargs):
intento = 1
while intento <= max_intentos:
try:
respuesta = funcion(*args, **kwargs)
print(respuesta.status_code)
print(funcion.__name__)
return respuesta
except Exception as e:
print(f"Intento {intento} falló. Args: {args}, Kwargs: {kwargs}")
intento += 1
return interno
return decorador
@con_reintentos(3)
def obtener_pagina(url, timeout=3):
print(f"Solicitando URL: {url}, timeout: {timeout}")
respuesta = requests.get(url=url, timeout=timeout)
print("Response from obtener_pagina >>", url)
return respuesta
urls = ["https://www.baidu.com/", "https://www.b.com/", "https://www.c.com/"]
for u in urls:
obtener_pagina(url=u, timeout=3)
# Salida (aproximada):
# Solicitando URL: https://www.baidu.com/, timeout: 3
# Response from obtener_pagina >> https://www.baidu.com/
# 200
# obtener_pagina
# Solicitando URL: https://www.b.com/, timeout: 3
# Intento 1 falló. Args: (), Kwargs: {'url': 'https://www.b.com/', 'timeout': 3}
# Solicitando URL: https://www.b.com/, timeout: 3
# Intento 2 falló. Args: (), Kwargs: {'url': 'https://www.b.com/', 'timeout': 3}
# ...
Decorador para métodos de clace
Al decorar un método de instancia, el decorador debe recibir explícitamente self como primer argumento:
def decorador_metodo(funcion):
def interno(self, *args, **kwargs):
resultado = funcion(self, *args, **kwargs)
return resultado
return interno
class Ejemplo:
@decorador_metodo
def saludar(self, nombre):
print(f"Hola {nombre} desde instancia")
Esta adaptación permite que el decorador funcione correctamente con métodos que necesitan acceder al objeto self.