Uso de *args y **kwargs en Python: ejemplos con decoradores

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.

Etiquetas: *args **kwargs Python decoradores reintentos

Publicado el 6-23 17:50