*args en parámetros de función


Podemos añadir *args en los parámetros de nuestra función para permitir que nuestra función acepte cualquier número de argumentos posicionales.

def hello(*args):
    print(args)

hello()        # ()
hello(1)       # (1,)
hello(1, 2)    # (1, 2)
hello(1, 2, 3, 4)  # (1, 2, 3, 4)

^ Observa que nuestra función hola recibe *args - podemos pasar cualquier número de argumentos a hola, y todos ellos quedarán recogidos en una tupla llamada args.

Nótese que podemos sustituir el nombre de la variable args por cualquier nombre de variable válido, pero el * tiene que estar ahí:

def hello(*stuff):
    print(stuff)

hello()        # ()
hello(1)       # (1,)
hello(1, 2)    # (1, 2)
hello(1, 2, 3, 4)  # (1, 2, 3, 4)

Si combinamos esto con parámetros normales, por ejemplo a & b, los parámetros normales se rellenarán primero, mientras que args tomará el resto de argumentos sin rellenar.

def hi(a, b, *args):
    print(f"{a=} {b=} {args=}")

hi()           # error
hi(1)          # error
hi(1, 2)       # a=1 b=2 args=()
hi(1, 2, 3, 4) # a=1 b=2 args=(3, 4)

^ aquí, si no pasamos nada a a o b, obtendremos errores como en los 2 primeros ejemplos. args captura todos los demás argumentos posicionales que no sean a y b.

**kwargs en parámetros de función


Si usamos ** en lugar de *, hacemos lo mismo para los argumentos de palabra clave - añadir **kwargs a nuestra función le permite aceptar cualquier número de argumentos de palabra clave.

CPU
1 vCPU
MEMORIA
1 GB
ALMACENAMIENTO
10 GB
TRANSFERENCIA
1 TB
PRECIO
$ 4 mes
Para obtener el servidor GRATIS debes de escribir el cupon "LEIFER"

Nota - si no está familiarizado, los argumentos de palabra clave se refieren a argumentos que pasamos utilizando un formato varname=value, como los que se muestran a continuación.

def hi(**kwargs):
    print(kwargs)

hi()                  # {}
hi(a=1)               # {'a': 1}
hi(a=1, b=2)          # {'a': 1, 'b': 2}
hi(a=1, b=2, c=3)     # {'a': 1, 'b': 2, 'c': 3}

^ todos los argumentos de palabras clave adicionales se recogerán en un diccionario llamado kwargs.

Una vez más, no tenemos que usar el nombre kwargs si no queremos - aunque yo uso por defecto el nombre kwargs por convención. El ** tiene que estar allí, sin embargo.

def hi(**things):
    print(things)

hi()                  # {}
hi(a=1)               # {'a': 1}
hi(a=1, b=2)          # {'a': 1, 'b': 2}
hi(a=1, b=2, c=3)     # {'a': 1, 'b': 2, 'c': 3}

Del mismo modo, podemos combinarlos con parámetros normales, pero tenemos que asegurarnos de que pasamos algo en los parámetros normales primero. kwargs entonces almacena todos los demás argumentos de palabras clave no utilizados en un diccionario.

def hi(a, b, **kwargs):
    print(f"{a=} {b=} {kwargs=}")

hi()                    # error
hi(a=1)                 # error
hi(a=1, b=2)            # a=1 b=2 kwargs={}
hi(a=1, b=2, c=3)       # a=1 b=2 kwargs={'c': 3}
hi(a=1, b=2, c=3, d=4)  # a=1 b=2 kwargs={'c': 3, 'd': 4}

^ si no pasamos nada a a o b, obtenemos errores, igual que en los 2 primeros ejemplos

*args en Argumentos de Funciones


También podemos usar * fuera de las funciones. Más específicamente, podemos usar * para descomprimir nuestro iterable (lista, tupla, etc) en los argumentos de nuestra función.

Aquí tenemos una función simple hi(a, b, c)

def hi(a, b, c):
    print(f"{a=} {b=} {c=}")

hi(1, 2, 3)        # a=1 b=2 c=3

mylist = [4, 5, 6]
hi(*mylist)        # a=4 b=5 c=6

podemos llamarlo normalmente usando hi(1, 2, 3)
también podemos pasar una lista de longitud 3 (mylist) con un * delante - hi (*mylist) es lo mismo que hi(4, 5, 6)
Como hi(a, b, c) requiere que se le pasen exactamente 3 argumentos, tenemos la libertad de pasarle 1 argumento posicional junto con *[5, 6], ya que esto hace 3 - sólo tenemos que asegurarnos de que se pasa el número correcto de argumentos.

def hi(a, b, c):
    print(f"{a=} {b=} {c=}")

mylist = [4, 5, 6]
hi(*mylist)        # a=4 b=5 c=6

mylist = [5, 6]
hi(4, *mylist)     # a=4 b=5 c=6

mylist = [4, 5, 6, 7, 8]
hi(*mylist)        # error

**kwargs en argumentos de función


Y si utilizamos ** en lugar de *, podemos hacer lo mismo con los diccionarios.

Aquí, hi (**mydict) es lo mismo que hi(a=4, b=5, c=6)

def hi(a, b, c):
    print(f"{a=} {b=} {c=}")

hi(a=4, b=5, c=6)        # a=4 b=5, c=6

mydict = {"a": 4, "b": 5, "c": 6}
hi(**mydict)             # a=4 b=5, c=6

Del mismo modo, también podemos combinar argumentos de palabras clave normales con **mydict siempre que pasemos todos los argumentos de palabras clave necesarios a nuestra función.

def hi(a, b, c):
    print(f"{a=} {b=} {c=}")

mydict = {"a": 4, "b": 5, "c": 6}
hi(**mydict)             # a=4 b=5, c=6

mydict = {"b": 5, "c": 6}
hi(a=4, **mydict)        # a=4 b=5, c=6

Cualquier parámetro después de * debe ser un argumento de palabra clave
Definamos una función con * solo

def hello(a, b, *, c, d):
    # c and d MUST be keyword arguments
    print(a, b, c, d)

hello(1, 2, c=3, d=4)      # 1 2 3 4
hello(a=1, b=2, c=3, d=4)  # 1 2 3 4
hello(1, 2, 3, 4)          # error

^ esta vez, * no se coloca antes de ningún parámetro - esto significa que cualquier parámetro definido después de * debe ser un argumento de palabra clave.

En este caso, c & d vienen después de *, así que c & d sólo pueden ser argumentos de palabra clave - a Python no le importa si pasamos argumentos posicionales o de palabra clave en a & b, pero c & d deben ser argumentos de palabra clave (o de lo contrario se produce un error)

Por el contrario, si reemplazamos * solo por / solo, esto significa que cualquier parámetro de función definido antes de / debe ser un argumento posicional.

def hello(a, b, /, c, d):
    # a and b MUST be positional keywords
    print(a, b, c, d)

hello(1, 2, c=3, d=4)      # 1 2 3 4
hello(1, 2, 3, 4)          # 1 2 3 4
hello(a=1, b=2, c=3, d=4)  # error

Podemos combinarlos para imponer restricciones adicionales a los parámetros de nuestras funciones.

def hola(a, b, /, c, d, *, e, f):
    # a and b must be positional arguments
    # c and d can be either
    # e and f must be keyword arguments
    print(a, b, c, d, e, f)

*args en el desempaquetado de tuplas


En Python, podemos utilizar el desempaquetado de tuplas para asignar de forma elegante múltiples variables a la vez.

person = ["bob", "m", 20]

name, gender, age = person
print(name)    # bob
print(gender)  # m
print(age)     # 20

Podemos combinar el desempaquetado de tuplas con * si tenemos variables sin usar.

Digamos que tenemos muchos más campos en persona, pero sólo necesitamos los 2 primeros por ahora - podemos usar *otros para asignar todo lo demás a la variable otros.

person = ["bob", "m", 20, 1.75, 70, "black"]

name, gender, *others = person
print(name)    # bob
print(gender)  # m
print(others)  # [20, 1.75, 70, 'black']

Podemos cambiar la posición de la variable con * también - en este ejemplo, sólo necesitamos el primer y último valor, así que ponemos *otros en el medio.

person = ["bob", "m", 20, 1.75, 70, "black"]

name, *others, hair = person
print(name)    # bob
print(hair)    # black
print(others)  # ['m', 20, 1.75, 70]

Y en este ejemplo, sólo necesitamos los 2 últimos valores, así que podemos poner *otros al principio.

person = ["bob", "m", 20, 1.75, 70, "black"]

*others, weight, hair = person
print(weight)  # 70
print(hair)    # black
print(others)  # ['bob', 'm', 20, 1.75]

Usar * al combinar iterables


También podemos utilizar * para combinar iterables como listas, tuplas, conjuntos, etc.

Aquí, combinamos 2 listas a & b desempaquetándolas en otra lista c

a = [1, 2]
b = [3, 4]
c = [*a, *b]

print(c)  # [1, 2, 3, 4]

Esto es muy potente, ya que podemos combinar esta técnica con valores normales como éste:

a = [1, 2]
b = [3, 4]
c = [-1, 0, *a, *b, 5, 6]

print(c)  # [-1, 0, 1, 2, 3, 4, 5, 6]

Nota - esto funciona también para otros iterables como conjuntos y tuplas.

** en la combinación de diccionarios


De forma similar, ** funciona para mapeos clave-valor de la misma forma que * funciona para valores individuales. Podemos utilizar ** para desempaquetar diccionarios en otros diccionarios con el fin de combinarlos.

x = {"a":1, "b":2}
y = {"c":3, "d":4}

z = {**x, **y}
print(z)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

Del mismo modo, también podemos combinar esta técnica con pares clave-valor normales al elaborar nuestro diccionario combinado.

x = {"a":1, "b":2}
y = {"c":3, "d":4}

z = {**x, **y, "e": 5}
print(z)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}

Importar todo desde un módulo


También podemos usar from modulename import * para decirle a Python que queremos importar todo lo que se pueda importar de algún módulo.

from colorama import *

# importing everything from 'colorama' module

Conclusión
Espero que hoy hayas aprendido algo nuevo.

Fuente