Argumentos de longitud variable en Python con * args y ** kwargs

    Introducci贸n

    Algunas funciones no tienen argumentos, otras tienen m煤ltiples. Hay ocasiones en que tenemos funciones con argumentos que no conocemos de antemano. Es posible que tengamos un n煤mero variable de argumentos porque queremos ofrecer una API flexible a otros desarrolladores o no conocemos el tama帽o de entrada. Con Python, podemos crear funciones para aceptar cualquier cantidad de argumentos.

    En este art铆culo, veremos c贸mo podemos definir y usar funciones con argumentos de longitud variable. Estas funciones pueden aceptar una cantidad desconocida de entrada, ya sea como entradas consecutivas o como argumentos con nombre.

    Usar muchos argumentos con * args

    Implementemos una funci贸n que encuentre el valor m铆nimo entre dos n煤meros. Se ver铆a as铆:

    def my_min(num1, num2):
        if num1 < num2:
            return num1
        return num2
    
    my_min(23, 50)
    
    23
    

    Simplemente verifica si el primer n煤mero es menor que el segundo. Si es as铆, se devuelve el primer n煤mero. De lo contrario, se devuelve el segundo n煤mero.

    Si deseamos encontrar un m铆nimo de 3 n煤meros, podemos agregar otro argumento a my_min() y m谩s declaraciones if. Si nuestra funci贸n m铆nima necesita encontrar el n煤mero m谩s bajo de cualquier cantidad indeterminada, podemos usar una lista:

    def my_min(nums):
        result = nums[0]
        for num in nums:
            if num < result:
                result = num
        return result
    
    my_min(4, 5, 6, 7, 2)
    
    2
    

    Si bien esto funciona, nuestros codificadores ahora tienen que incluir sus n煤meros en una lista, lo que no es tan sencillo como lo era cuando ten铆amos dos o tres argumentos definidos. Obtengamos lo mejor de ambos mundos con argumentos de longitud variable.

    Los argumentos de longitud variable, varargs para abreviar, son argumentos que pueden requerir una cantidad de entrada no especificada. Cuando se utilizan, el programador no necesita envolver los datos en una lista o secuencia alternativa.

    En Python, los varargs se definen usando el *args sintaxis. Reimplementemos nuestro my_min() funcionar con *args:

    def my_min(*args):
        result = args[0]
        for num in args:
            if num < result:
                result = num
        return result
    
    my_min(4, 5, 6, 7, 2)
    
    2
    

    Nota: args es solo un nombre, puede nombrar ese vararg cualquier cosa siempre que est茅 precedido por un solo asterisco (*). Es una buena pr谩ctica seguir nombr谩ndolo args para que sea inmediatamente reconocible.

    Cualquier argumento que venga despu茅s *args debe ser un argumento con nombre, un argumento al que se hace referencia por su nombre en lugar de su posici贸n. Con *args ya no puede hacer referencia a otro argumento por su posici贸n.

    Adem谩s, solo puedes tener en *args escriba vararg en una funci贸n.

    Puede estar pensando que la soluci贸n con *args es muy similar a la soluci贸n de lista. Eso es porque *args es internamente una Tupla, que es una secuencia iterable similar a las listas. Si desea verificar su tipo, puede ingresar el c贸digo en su int茅rprete de Python:

    $ python3
    >>> def arg_type_test(*args):
    ...     print(type(args))
    ...
    >>> arg_type_test(1, 2)
    <class 'tuple'>
    

    Con *args, podemos aceptar varios argumentos en secuencia como se hace en my_min(). Estos argumentos son procesados 鈥嬧媝or su posici贸n. 驴Qu茅 pasar铆a si quisi茅ramos tomar varios argumentos, pero hacer referencia a ellos por su nombre? Veremos c贸mo hacer esto en la siguiente secci贸n.

    Uso de muchos argumentos con nombre con ** kwargs

    Python puede aceptar m煤ltiples argumentos de palabras clave, mejor conocidos como **kwargs. Se comporta de manera similar a *args, pero almacena los argumentos en un diccionario en lugar de tuplas:

    def kwarg_type_test(**kwargs):
        print(kwargs)
    
    kwarg_type_test(a="hi")
    kwarg_type_test(roses="red", violets="blue")
    

    La salida ser谩:

    {'a': 'hi'}
    {'roses': 'red', 'violets': 'blue'}
    

    Usando un diccionario, **kwargs puede conservar los nombres de los argumentos, pero no podr铆a mantener su posici贸n.

    Nota: Me gusta args, puede utilizar cualquier otro nombre que kwargs. Sin embargo, la mejor pr谩ctica dicta que debe usar constantemente kwargs.

    Ya que **kwargs es un diccionario, puede iterar sobre ellos como cualquier otro usando el .items() m茅todo:

    def kwargs_iterate(**kwargs):
        for i, k in kwargs.items():
            print(i, '=', k)
    
    kwargs_iterate(hello='world')
    

    Cuando se ejecuta, nuestra consola mostrar谩:

    hello = world
    

    Los argumentos de palabras clave son 煤tiles cuando no est谩 seguro de si un argumento estar谩 disponible. Por ejemplo, si tuvi茅ramos una funci贸n para guardar una publicaci贸n de blog en una base de datos, guardar铆amos la informaci贸n como el contenido y el autor. Una publicaci贸n de blog puede tener etiquetas y categor铆as, aunque no siempre se establecen.

    Podemos definir una funci贸n como esta:

    def save_blog_post(content, author, tags=[], categories=[]):
        pass
    

    Alternativamente, permitimos que la funci贸n que llama pase cualquier cantidad de argumentos y solo asocie tags y categories si est谩n configurados:

    def save_blog_post(content, author, **kwargs):
        if kwargs.get('tags'):
            # Save tags with post
            pass
    
        if kwargs.get('categories'):
            # Save categories with post
            pass
    

    Ahora que tenemos una comprensi贸n de ambos tipos de soporte para argumentos de longitud variable, veamos c贸mo podemos combinar los dos en una funci贸n.

    Combinando varargs y argumentos de palabras clave

    Muy a menudo queremos usar ambos *args y **kwargs juntos, especialmente al escribir bibliotecas Python o c贸digo reutilizable. Por suerte para nosotros, *args y **kwargs juegan bien juntos, y podemos usarlos de la siguiente manera:

    def combined_varargs(*args, **kwargs):
        print(args)
        print(kwargs)
    
    combined_varargs(1, 2, 3, a="hi")
    

    Si ejecuta ese fragmento de c贸digo, ver谩:

    (1, 2, 3)
    {'a': 'hi'}
    

    Al mezclar los argumentos posicionales y nombrados, los argumentos posicionales deben venir antes de argumentos nombrados. Adem谩s, los argumentos de longitud fija se anteponen a los argumentos de longitud variable. Por lo tanto, obtenemos un pedido como este:

    • Argumentos posicionales conocidos
    • *args
    • Argumentos con nombre conocidos
    • **kwargs

    Una funci贸n con todo tipo de argumentos puede verse as铆:

    def super_function(num1, num2, *args, callback=None, messages=[], **kwargs):
        pass
    

    Una vez que sigamos ese orden al definir y llamar a funciones con varargs y argumentos de palabras clave, obtendremos el comportamiento que esperamos de ellos.

    Hasta ahora hemos usado el *args y **kwargs sintaxis para las definiciones de funciones. Python nos permite usar la misma sintaxis cuando tambi茅n llamamos funciones. 隆Veamos c贸mo!

    Desempaquetado de argumentos con * args y ** kwargs

    Consideremos una funci贸n add3(), que acepta 3 n煤meros e imprime su suma. Podemos crearlo as铆:

    def add3(num1, num2, num3):
        print("The grand total is", num1 + num2 + num3)
    

    Si ten铆a una lista de n煤meros, puede usar esta funci贸n especificando qu茅 elemento de la lista se usa como argumento:

    magic_nums = [32, 1, 7]
    
    add3(magic_nums[0], magic_nums[1], magic_nums[2])
    

    Si ejecuta este c贸digo, ver谩:

    The grand total is 40
    

    Si bien esto funciona, podemos hacerlo m谩s sucinto con *args sintaxis:

    add3(*magic_nums)
    

    La salida es The grand total is 40, justo como antes.

    Cuando usamos *args sintaxis en una llamada a funci贸n, estamos desempaquetando la variable. Al desempacar, queremos decir que estamos sacando los valores individuales de la lista. En este caso, sacamos cada elemento de la lista y los colocamos en los argumentos, donde la posici贸n 0 corresponde al primer argumento.

    Tambi茅n puede descomprimir una tupla de manera similar:

    tuple_nums = (32, 1, 7)
    add3(*tuple_nums) # The grand total is 40
    

    Si desea descomprimir un diccionario, debe usar el **kwargs sintaxis.

    dict_nums = {
        'num1': 32,
        'num2': 1,
        'num3': 7,
    }
    
    add3(**dict_nums) # The grand total is 40
    

    En este caso, Python hace coincidir la clave del diccionario con el nombre del argumento y establece su valor.

    隆Y eso es! Puede administrar m谩s f谩cilmente sus llamadas a funciones desempaquetando valores en lugar de especificar cada argumento que necesita un valor de un objeto.

    Conclusi贸n

    Con Python, podemos usar el *args o **kwargs sintaxis para capturar un n煤mero variable de argumentos en nuestras funciones. Utilizando *args, podemos procesar un n煤mero indefinido de argumentos en la posici贸n de una funci贸n. Con **kwargs, podemos recuperar un n煤mero indefinido de argumentos por su nombre.

    Si bien una funci贸n solo puede tener un argumento de longitud variable de cada tipo, podemos combinar ambos tipos de funciones en un solo argumento. Si lo hacemos, debemos asegurarnos de que los argumentos posicionales vengan antes que los argumentos con nombre y que los argumentos fijos vengan antes que los de longitud variable.

    Python tambi茅n nos permite usar la sintaxis para llamadas a funciones. Si tenemos una lista o una tupla y usamos el *args sintaxis, descomprimir谩 cada valor como argumentos posicionales. Si tenemos un diccionario y usamos **kwargs sintaxis, entonces har谩 coincidir los nombres de las claves del diccionario con los nombres de los argumentos de la funci贸n.

    驴Est谩s trabajando en una funci贸n que pueda beneficiarse de este tipo de argumentos? 驴O quiz谩s puede refactorizar una funci贸n y hacerla a prueba de futuro? 隆H谩ganos saber en qu茅 est谩 trabajando!

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *