El uso de *args y **kwargs en Python

El uso de *args y **kwargs en Python

El uso de *args y **kwargs en Python - 100 días de Python #22

En este capítulo te voy a mostrar con unos ejemplos prácticos el uso de *args y **kwargs en Python.

Vamos a dejar por un capítulo en espera el tema de MySQL y Python para enseñarte algo que debes conocer antes de enfrentarte al reto que tengo pensado hacer. En el siguiente capítulo, empezaremos a utilizar la programación orientada a objetos junto con la programación modular, interfaces gráficas con Tkinter y MySQL, por eso, debo prepararte bien, ya que lo que aprenderás hoy, lo aplicaremos en ese y en otros capítulos.

¿Qué son *args y **kwargs?

Son dos elementos de Python, que nos van a permitir utilizar un número indefinido de argumentos en las funciones.

Como bien sabrás, si has seguido mi curso hasta aquí, cuando creamos una función o un método, los argumentos que tenemos que pasar, son los declarados en dicho método o función. Si espera 4, hay que pasarle 4, ni uno más, ni uno menos.

¿Qué ocurre cuando esto no nos sirve? Hay ocasiones en las que necesitaremos que una función obtenga diferente número de valores, unas veces 2, otras veces 5, otras 3, etc.

Es aquí donde entran en juego *args y **kwargs.

El uso de *args en Python

Empecemos por el más sencillo de los dos, *args.

El nombre args, viene de argumentos. Te lo digo por si así lo memorizas más fácil, no obstante, el nombre args o kwargs son por convención, puedes poner *valores que funcionará igual. Lo único, es dejar el * en el caso de *args o ** en el caso de **kwargs.

El parámetro especial *args permite en una función, pasar opcionalmente, un número variable de argumentos posicionales.

Ejemplo con *args de Python

En el siguiente ejemplo tenemos el uso de *args.

def prueba(*args):
    valor = 0
    for i in args:
        valor += 1
        print(f"El argumento número {valor} es: {i}")
    
prueba("rojo", "azul", "verde", "amarillo")

Resultado en la consola

El argumento número 1 es: rojo
El argumento número 2 es: azul
El argumento número 3 es: verde
El argumento número 4 es: amarillo

No importa el número de argumentos que le pasemos, los acepta todos. Incluso si no pones ninguno. En este caso, no da error, pero tampoco saca nada en la consola, puesto que no hay argumentos.

def prueba(*args):
    valor = 0
    for i in args:
        valor += 1
        print(f"El argumento número {valor} es: {i}")
    
prueba()

Para comprobar lo que devuelve args, podemos imprimirlo, así sabrás que es lo que está iterando el bucle de este ejemplo.

def prueba(*args):
    print(args)
    
prueba("rojo", "azul", "verde", "amarillo")

Pues bien, es una tupla con un argumento por posición. Aquí está la trampa. Creemos que le estamos pasando los argumentos que queremos, cuando es una mera ilusión, ya que en realidad, le pasamos uno solo, la tupla. La cuestión es que funciona, y ahora sabes como.

Resultado en la consola

('rojo', 'azul', 'verde', 'amarillo')

El uso de **kwargs en Python

Llegamos al talón de Aquiles de quienes empiezan a programar en Python y se enfrentan a estos dos titanes (*args y **kwargs). El uso de kwargs se le atraganta a la mayoría. Esto es porque normalmente, se explica de una forma, bajo mi punto de vista, incorrecta. Voy a ver si puedo simplificar al máximo el empleo de este elemento.

**kwargs es lo mismo que *args, solo que en este caso, permite un número de argumentos variables, pero con clave (key) de ahí su nombre, keyword (kw) arguments (args), argumentos de clave.

Recuerda que los argumentos posicionales eran los que hemos utilizado en el ejemplo anterior y que los argumentos de clave, eran los que no tenían una posición fija (argumento = valor).

Como puedes ver, no tiene mucha pérdida. Ahora vayamos a la prueba de fuego, su uso en un ejemplo. Para ello, volvemos a los diccionarios, los cuales, están ligados al uso de **kwargs, dado que lo que devuelve es un diccionario en lugar de una tupla como pasa con *args.

Con esta combinación, podemos trabajar mejor con los dos tipos de argumentos.

Recuerda:

  • *args = tupla de argumentos posicionales indefinidos (en cuanto a número de ellos).
  • **kwargs = diccionario de argumentos de clave indefinidos (en cuanto a número de ellos).

El método keys() de Python

El método keys() de Python, es un método empleado para obtener solo las claves de un diccionario.

Recuerda

En el capítulo 7 del curso, hablé y mostré ejemplos de diccionarios Python. Te recomiendo que le eches un repaso rápido si no lo recuerdas muy bien.

Aquí tienes un ejemplo de uso con **kwargs:

def claves(**kwargs):
    numero = 0
    for clave in kwargs.keys():
        numero +=1
        print(f"Clave {numero}: {clave}.")

claves(nombre="Javier", apellidos="Gómez de la barca", edad="27")

Resultado en la consola

Clave 1: nombre.
Clave 2: apellidos.
Clave 3: edad.

El número de argumentos de clave, como he mencionado, es indefinido, puedes poner los que quieras.

Antes de seguir, imprimamos en la consola kwargs directamente, para ver si, efectivamente, devuelve un diccionario.

def diccionario(**kwargs):
    print(kwargs)

diccionario(nombre="Javier", apellidos="Gómez de la barca", edad="27")

Ahí lo tenemos. Nos guarda todos los argumentos de clave, en forma de diccionario.

Resultado en la consola

{'nombre': 'Javier', 'apellidos': 'Gómez de la barca', 'edad': '27'}

El método values() de Python

Si tenemos en Python, un método para obtener claves de diccionarios, también tenemos que tener otro para obtener los valores. Pues es este, values().

def valores(**kwargs):
    numero = 0
    for valor in kwargs.values():
        numero +=1
        print(f"Valor {numero}: {valor}.")

valores(nombre="Javier", apellidos="Gómez de la barca", edad="27")

Resultado en la consola

Valor 1: Javier.
Valor 2: Gómez de la barca.
Valor 3: 27.

El método items() de Python

Ahora, puede que te plantees la forma de obtener tanto la clave como el valor.

Con el método items(), nos va a devolver un valor y una clave. Todo de una vez.

def valor_clave(**kwargs):
    numero = 0
    for resultado in kwargs.items():
            numero +=1
            print(f"Argumento {numero}: {resultado}.")

valor_clave(nombre="Javier", apellidos="Gómez de la barca", edad="27")

Resultado en la consola

Argumento 1: ('nombre', 'Javier').
Argumento 2: ('apellidos', 'Gómez de la barca').
Argumento 3: ('edad', '27').

De esta forma, nos devuelve cada par (clave:valor) en una tupla.

La función predefinida dict()

En Python, contamos con una función predefinida (built-in function) llamada dict(), la cual, es capaz de crear un diccionario a partir de argumentos de clave. Es, básicamente, otra forma de producir diccionarios.

dict(nombre="Javier", apellidos="Gómez de la barca", edad="27")

La forma que te había enseñado hasta el momento, era esta:

{"nombre":"Javier", "apellidos":"Gómez de la barca", "edad":"27"}

Como puedes ver, con dict() es más cómodo escribir las claves, ya que estas, no tienen que ir entre comillas, aunque sean de tipo string. La forma de acceder a las claves y valores, es la misma en ambos casos.

Pasar un diccionario externo a una función

Gracias a **kwargs, se puede crear una función que acepte directamente un diccionario entero como argumento.

Fíjate, que en este caso, para pasarle a la llamada el diccionario, hay que ponerle los dos asteriscos para que se tome como **kwargs.

def imprime_diccionario(**kwargs):
    for elemento in kwargs.items():
        print(elemento)
    
usuario1 = {"nombre":"Javier", "apellidos":"Gómez de la barca", "edad":"27"}

imprime_diccionario(**usuario1)

Un ejemplo que te puedo dar de uso a esto, es crear un diccionario con los valores de conexión MySQL y utilizarlo de esta forma. Esto, seguramente, lo veamos en el siguiente capítulo.

Utilizar parámetros fijos junto con *args y **kwargs

Otra cosa que podemos hacer, es utilizar parámetros fijos en las funciones junto con *args y **kwargs. Aquí tienes un ejemplo de esto con *args:

def multiplicar(x,y,*args):
    return(x * y * args[0] * args[1])

print(multiplicar(10,4,50,6))

Resultado en la consola

12000

En este caso, tengo dos parámetros ("'x" e "y") fijos y luego está *args, que me permite poner tantos argumentos más dentro de la función como quiera.

El orden es importante, primero deben ir los parámetros fijos en la declaración de la función y después *args o **kwargs, si los pones como en el siguiente ejemplo, producirás un error:

def multiplicar(x,*args, y):
    return(x * y * args[0] * args[1])

print(multiplicar(10,4,50,6))

Error en la consola

TypeError: multiplicar() missing 1 required keyword-only argument: 'y'

El error se producirá en la llamada a la función. Si nos detenemos a pensar, nos dice que falta un argumento (y) porque le estoy pasando a "x" el argumento 10, 4, 50 y 6, lo obtiene *args, que es "infinito", por lo tanto, de esta forma, "y" nunca podrá obtener un argumento en una llamada.

Lo mismo ocurrirá con *kwargs.

¿Se puede utilizar *args junto con **kwargs?

Sí, se puede utilizar. Aquí tienes un ejemplo:

def datos(*args, **kwargs):
    print(args)
    print(kwargs)
    
usuario1 = {"nombre":"Javier", "apellidos":"Gómez de la barca", "edad":"27"}
    
datos(10,50,60, **usuario1)

Resultado en la consola

(10, 50, 60)
{'nombre': 'Javier', 'apellidos': 'Gómez de la barca', 'edad': '27'}

Primero, le damos todos los argumentos que necesitemos en la llamada, la parte del *args. Después, se le pasa un diccionario al parámetro **kwargs. El intérprete de Python "entiende" lo que queremos hacer.

No obstante, debes seguir este orden, primero *args y luego **kwargs. Si lo pones al revés, produces un error:

def datos(**kwargs, *args):
    print(args)
    print(kwargs)
    
usuario1 = {"nombre":"Javier", "apellidos":"Gómez de la barca", "edad":"27"}
    
datos(**usuario1,10,50,60)

Error en la consola

SyntaxError: arguments cannot follow var-keyword argument

Sencillamente, nos indica que los argumentos (normales o *args) no pueden seguir colocarse después de **kwargs.

Convención de nombres *args y **kwargs

Te he mencionado ya, que por convención, se escribe *args y **kwargs en las funciones, pero esto da igual. Aquí tienes un ejemplo:

def datos(*argumentos, **argumentos_clave):
    print(argumentos)
    print(argumentos_clave)
    
usuario1 = {"nombre":"Javier", "apellidos":"Gómez de la barca", "edad":"27"}
    
datos(10,50,60, **usuario1)

Resumen de todo el capítulo

  • Utiliza *args para pasar un número "ilimitado" de parámetros.
  • Emplea **kwargs para pasar un número ilimitado de argumentos de clave o un diccionario (ya que se puede usar como argumentos de clave cada uno de sus pares (clave-valor)).

Dejamos el capítulo aquí. Iré explicando este tema con más ejemplos en capítulos posteriores, cuando le podamos dar un uso real a todo esto, ya que, seguramente, te quedan muchas dudas y no le ves prácticamente la utilidad a esto. Por el momento, puedes practicar un poco con estos ejercicios, para ver si has entendido correctamente de qué forma funcionan *args y **kwargs.

No te pierdas nada de todo el contenido que tengo sobre Python.

Más que suficiente para este capítulo 22. Pasemos ya a la parte de los ejercicios de Python relacionados con el temario.

Un comentario en «El uso de *args y **kwargs en Python»

Deja una respuesta

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

curso Java Entrada anterior Los bloques de código e indentaciones en Java
curso de Python Entrada siguiente Soluciones de ejercicios de Python con *args y **kwargs