python

Entendiendo *args y **kwargs en Python

En este post explicaré para qué sirven y cómo se usan los *args y **kwargs en las llamadas a las funciones de Python 3.

Explicación de args y kwargs en Python. Función promedio

Esto permite llamar a funciones que reciben un número indefinido de argumentos; y también cuando necesitamos múltiples argumentos opcionales. Vamos a ver algunos ejemplos.

Las funciones y los argumentos

Recordemos que las funciones son algo así:

def funcion(un_argumento)

Y que pueden tener más de un argumento:

def sumar(numero1, numero2)

O no tener ninguno:

def funcion()

Dentro de las funciones, se pueden usar a los argumentos para lo que sea necesario.

Explicando *args en Python

Este nombre no es obligatorio (la sintaxis sí), podría ser, por ejemplo, *argumentos o cualquier otra cosa siempre y cuando se ponga el asterisco antes del nombre.

Para ejemplificar esto, pongamos el ejemplo de una función que calcule el promedio de algunos valores. ¿Cuántos valores? no sabemos, por eso es que recibimos infinitos argumentos gracias a *args.

Dejaré que el código hable por mí:

"""
 Explicar *args en Python con una
 función
 @author parzibyte
"""

def promedio(*numeros):
    #Saber cuántos elementos hay, imaginemos que los argumentos son un arreglo
    cantidad_de_elementos = len(numeros)
    #Evitar división entre 0
    if cantidad_de_elementos <= 0:
        return 0
    suma = 0
    # Justo aquí iteramos los argumentos o *args
    for numero in numeros:
        suma += numero
    return suma / cantidad_de_elementos


print(promedio(80, 97, 114, 122, 105, 98, 117, 103)) #104.5
print(promedio(50, 20)) # 35
print(promedio(1, 5)) # 3
print(promedio(20, 1500)) # 760

La función es definida, pero con un argumento un poco especial: *numeros. Eso le indica a Python que sea una función que puede recibir desde 0 hasta infinitos argumentos. Así, se puede invocar a la misma con cualquier número de argumentos.

En este caso fue con argumentos enteros, pero podrían ser cadenas, URL’s de imágenes para descargar, etcétera. Lo importante o interesante es que podemos recorrer los argumentos

No es que esto sea algo nuevo, las funciones con un número indefinido

Más sobre *args en Python

Sólo para mencionar que podemos tener una función que reciba tanto un argumento posicional y los demás infinitos.

Por ejemplo, una que calcule promedios y reciba el nombre de una persona (sólo para ejemplificar) quedaría así:

def promedio(nombre, *argumentos)

Por cierto, no se puede tener dos veces a *args. Lo siguiente es erróneo:

def funcion(*argumentos, *otros_argumentos)

Y no se pueden tener a argumentos posicionales después de los argumentos infinitos. Lo siguiente no es correcto:

def function(*argumentos, un_argumento)

Uso de **kwargs en Python

Igualmente no es necesario poner kwargs, sino los dos asteriscos.

Por ejemplo, podría usarse el nombre de **argumentos_con_nombre ya que más o menos eso quieren decir (no literalmente, porque en inglés significa keyword arguments)

Estos kwargs en Python son usados para dos cosas que veremos a continuación.

Cuando una función recibe muchos argumentos

Por ejemplo, una función que conecte a MongoDB desde Python. Supongamos que queda así:

def conectar(host, puerto, usuario, palabra_secreta, nombre_base_de_datos)

Sé que no es tan larga, pero imaginemos que más adelante le agregamos más parámetros. Quedaría muy larga, mucho más. Así que mejor la definimos así:

def conectar(**opciones)

Veamos el código:

"""
 Explicar **kwargs en Python con una
 función
 @author parzibyte
"""


def conectar(**opciones):
    host = opciones["host"]
    puerto = opciones["puerto"]
    usuario = opciones["usuario"]
    palabra_secreta = opciones["palabra_secreta"]
    nombre_base_de_datos = opciones["nombre_base_de_datos"]
    print(
        "Host: {}, puerto: {}, usuario: {}, palabra_secreta: {}, nombre_base_de_datos: {}"
        .format(host, puerto, usuario, palabra_secreta, nombre_base_de_datos))

En él indicamos que vamos a recibir una lista de argumentos con nombre con los dos asteriscos **, y en la función accedemos a ellos como cuando accedemos a un diccionario.

Ahora vemos la llamada, la cual puede ser primero definiendo un diccionario y pasándolo con **:

argumentos = {
    "host": "127.0.0.1",
    "puerto": "27017",
    "usuario": "parzibyte",
    "palabra_secreta": "hunter2",
    "nombre_base_de_datos": "agenda",
}
conectar(**argumentos)

El código de arriba confunde un poco, porque tenemos que usar ** al llamar a la función, aparte de cuando la definimos. La otra opción que es más expresiva y legible es la siguiente:

conectar(
    host="12.34.56.78",
    puerto="27017",
    usuario="parzibyte",
    palabra_secreta="hunter2",
    nombre_base_de_datos="agenda")

A mi parecer es más legible, y no hay necesidad de definir un diccionario. Además, el comportamiento dentro de la función es el mismo, al final es un diccionario que tiene claves y valores.

En caso de tener múltiples argumentos, pero que algunos sean opcionales

Ahora veamos otro uso clave de kwargs en Python. Esto va combinado con lo que vimos acerca de switch en Pyhton.

¿Qué pasa si algunos argumentos tienen valores por defecto o son opcionales? muy fácil, usamos al método get  de los diccionarios y proveemos un valor por defecto, algo así:

"""
 Explicar **kwargs en Python con una
 función
 @author parzibyte
"""


def conectar(**opciones):
    host = opciones.get("host", "localhost")
    puerto = opciones.get("puerto", "27017")
    usuario = opciones.get("usuario",
                           "root?")  # Aunque root no existe en MongoDB, creo
    palabra_secreta = opciones.get("palabra_secreta", "123")
    nombre_base_de_datos = opciones.get("nombre_base_de_datos", "test")
    print(
        "Host: {}, puerto: {}, usuario: {}, palabra_secreta: {}, nombre_base_de_datos: {}"
        .format(host, puerto, usuario, palabra_secreta, nombre_base_de_datos))

De esta manera, aunque no se proporcionen todos los datos (por ejemplo, pasar únicamente el usuario y la contraseña), no habrá errores:

conectar(usuario="parzibyte", palabra_secreta="hunter2")

En ese caso se tomarán los demás valores por defecto. Cabe mencionar que no tenemos que pasar a los argumentos en orden, lo de arriba igualmente podría ser así:

conectar(palabra_secreta="hunter2", usuario="parzibyte")

Esa es una de las ventajas de **kwargs en Python.

Diferencia entre **kwargs y *args en Python

Los **kwargs tienen nombre, aunque estén desordenados, pero tienen un nombre. En cambio, *args es una lista plana, no se sabe qué valor se encuentra en determinada posición.

Infiero que los keyword arguments son usados para definir valores por defecto en funciones que toman muchos de los mismos.

Usar argumentos, **kwargs y *args en Python en la misma función

Justo ahora no se me ocurre un uso, pero claro que se pueden usar en conjunto. Sólo recuerda estas reglas:

  1. Primero van los argumentos posicionales
  2. Luego, opcionalmente, va *args
  3. Finalmente, va **kwargs

Veamos este ejemplo:

"""
 Explicar **kwargs en Python con una
 función
 @author parzibyte
"""


def conectar(nombre, *numeros, **opciones):
    print("Hola, ", nombre)
    for numero in numeros:
        print(numero)
    host = opciones.get("host", "localhost")
    puerto = opciones.get("puerto", "27017")
    usuario = opciones.get("usuario",
                           "root?")  # Aunque root no existe en MongoDB, creo
    palabra_secreta = opciones.get("palabra_secreta", "123")
    nombre_base_de_datos = opciones.get("nombre_base_de_datos", "test")
    print(
        "Host: {}, puerto: {}, usuario: {}, palabra_secreta: {}, nombre_base_de_datos: {}"
        .format(host, puerto, usuario, palabra_secreta, nombre_base_de_datos))


conectar("Luis", 1, 2, 3, 4, usuario="parzibyte", palabra_secreta="hunter2")

En ese caso el argumento posicional es nombre, pero podría haber más. Después definimos los argumentos indefinidos, y finalmente pasamos los **kwargs. Recuerda que en lugar de estos últimos, podríamos definir un diccionario y pasarlo con **diccionario.

¿Quieres probar el código? puedes descargarlo y ejecutarlo, pero primero instala el intérprete de Python.

Espero que haya quedado claro. Si tienes dudas, ahí están los comentarios.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Ver comentarios

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.