Unpacking en Python: Más allá de la asignación paralela

    Introducción

    Desempaquetar en Python se refiere a una operación que consiste en asignar un iterable de valores a una tupla (o list) de variables en una sola declaración de asignación. Como complemento, el embalaje término puede ser utilizado cuando recogemos varios valores en una sola variable utilizando el operador de desembalaje iterable, *.

    Históricamente, los desarrolladores de Python se han referido genéricamente a este tipo de operación como desempaquetado de tuplas. Sin embargo, dado que esta característica de Python resultó ser bastante útil y popular, se ha generalizado a todo tipo de iterables. Hoy en día, un término más moderno y preciso sería desembalaje iterable.

    En este tutorial, aprenderemos qué es el desempaquetado iterable y cómo podemos aprovechar esta característica de Python para hacer que nuestro código sea más legible, fácil de mantener y Pythonico.

    Además, también cubriremos algunos ejemplos prácticos de cómo usar la función de desempaquetado iterable en el contexto de operaciones de asignaciones, forbucles, definiciones de funciones y llamadas a funciones.

    Empaquetar y desempacar en Python

    Python permite que una tuple(o list) variables aparezcan en el lado izquierdo de una operación de asignación. Cada variable en el tuplepuede recibir un valor (o más, si usamos el *operador) de un iterable en el lado derecho de la asignación.

    Por razones históricas, los desarrolladores de Python solían llamar a esta tupla desempaquetado. Sin embargo, dado que esta característica se ha generalizado a todo tipo de iterables, un término más preciso sería desembalaje iterable y así lo llamaremos en este tutorial.

    Las operaciones de desempaquetado han sido bastante populares entre los desarrolladores de Python porque pueden hacer que nuestro código sea más legible y elegante. Echemos un vistazo más de cerca al desempaquetado en Python y veamos cómo esta función puede mejorar nuestro código.

    Desembalaje de tuplas

    En Python, podemos poner un tuplede variables en el lado izquierdo de un operador de asignación ( =) y un tuplede valores en el lado derecho. Los valores de la derecha se asignarán automáticamente a las variables de la izquierda según su posición en el tuple. Esto se conoce comúnmente como desempaquetado de tuplas en Python. Mira el siguiente ejemplo:

    >>> (a, b, c) = (1, 2, 3)
    >>> a
    1
    >>> b
    2
    >>> c
    3
    

    Cuando colocamos tuplas a ambos lados de un operador de asignación, se lleva a cabo una operación de desempaquetado de tuplas. Los valores de la derecha se asignan a las variables de la izquierda según su posición relativa en cada una tuple. Como puede ver en el ejemplo anterior, aserá 1, bserá 2y cserá 3.

    Para crear un tupleobjeto, no necesitamos usar un par de paréntesis ()como delimitadores. Esto también funciona para el desempaquetado de tuplas, por lo que las siguientes sintaxis son equivalentes:

    >>> (a, b, c) = 1, 2, 3
    >>> a, b, c = (1, 2, 3)
    >>> a, b, c = 1, 2, 3
    

    Dado que todas estas variaciones son sintaxis Python válida, podemos usar cualquiera de ellas, dependiendo de la situación. Podría decirse que la última sintaxis se usa más comúnmente cuando se trata de descomprimir en Python.

    Cuando desempaquetamos valores en variables usando el desempaquetado de tuplas, el número de variables del lado izquierdo tupledebe coincidir exactamente con el número de valores del lado derecho tuple. De lo contrario, obtendremos un ValueError.

    Por ejemplo, en el siguiente código, usamos dos variables a la izquierda y tres valores a la derecha. Esto nos indicará ValueErrorque hay demasiados valores para descomprimir:

    >>> a, b = 1, 2, 3
    Traceback (most recent call last):
      ...
    ValueError: too many values to unpack (expected 2)
    

    Nota: La única excepción a esto es cuando usamos el *operador para empaquetar varios valores en una variable, como veremos más adelante.

    Por otro lado, si usamos más variables que valores, obtendremos un ValueErrorpero esta vez el mensaje dice que no hay suficientes valores para descomprimir:

    >>> a, b, c = 1, 2
    Traceback (most recent call last):
      ...
    ValueError: not enough values to unpack (expected 3, got 2)
    

    Si usamos un número diferente de variables y valores en una operación de desempaquetado de tuplas, obtendremos un ValueError. Eso es porque Python necesita saber sin ambigüedades qué valor entra en qué variable, para que pueda hacer la asignación en consecuencia.

    Desembalaje de Iterables

    La función de desempaquetado de tuplas se hizo tan popular entre los desarrolladores de Python que la sintaxis se amplió para que funcione con cualquier objeto iterable. El único requisito es que el iterable rinda exactamente un artículo por variable en el receptor tuple(o list).

    Vea los siguientes ejemplos de cómo funciona el desempaquetado iterable en Python:

    >>> # Unpacking strings
    >>> a, b, c="123"
    >>> a
    '1'
    >>> b
    '2'
    >>> c
    '3'
    >>> # Unpacking lists
    >>> a, b, c = [1, 2, 3]
    >>> a
    1
    >>> b
    2
    >>> c
    3
    >>> # Unpacking generators
    >>> gen = (i ** 2 for i in range(3))
    >>> a, b, c = gen
    >>> a
    0
    >>> b
    1
    >>> c
    4
    >>> # Unpacking dictionaries (keys, values, and items)
    >>> my_dict = {'one': 1, 'two':2, 'three': 3}
    >>> a, b, c = my_dict  # Unpack keys
    >>> a
    'one'
    >>> b
    'two'
    >>> c
    'three'
    >>> a, b, c = my_dict.values()  # Unpack values
    >>> a
    1
    >>> b
    2
    >>> c
    3
    >>> a, b, c = my_dict.items()  # Unpacking key-value pairs
    >>> a
    ('one', 1)
    >>> b
    ('two', 2)
    >>> c
    ('three', 3)
    

    Cuando se trata de desempaquetar en Python, podemos usar cualquier iterable en el lado derecho del operador de asignación. El lado izquierdo puede llenarse con una tupleo con una listde variables. Vea el siguiente ejemplo en el que usamos a tupleen el lado derecho de la declaración de asignación:

    >>> [a, b, c] = 1, 2, 3
    >>> a
    1
    >>> b
    2
    >>> c
    3
    

    Funciona de la misma manera si usamos el range()iterador:

    >>> x, y, z = range(3)
    >>> x
    0
    >>> y
    1
    >>> z
    2
    

    Aunque esta es una sintaxis válida de Python, no se usa comúnmente en código real y quizás un poco confusa para los desarrolladores principiantes de Python.

    Finalmente, también podemos usar setobjetos en operaciones de desempaquetado. Sin embargo, dado que los conjuntos son una colección desordenada, el orden de las asignaciones puede ser algo incoherente y puede dar lugar a errores sutiles. Mira el siguiente ejemplo:

    >>> a, b, c = {'a', 'b', 'c'}
    >>> a
    'c'
    >>> b
    'b'
    >>> c
    'a'
    

    Si usamos conjuntos en las operaciones de desempaquetado, entonces el orden final de las asignaciones puede ser bastante diferente de lo que queremos y esperamos. Por lo tanto, es mejor evitar el uso de conjuntos en las operaciones de desempaquetado, a menos que el orden de asignación no sea importante para nuestro código.

    Embalaje con el operador *

    El *operador se conoce, en este contexto, como el operador de desempaquetado de tuplas (o iterables). Extiende la funcionalidad de desempaquetado para permitirnos recopilar o empaquetar múltiples valores en una sola variable. En el siguiente ejemplo, empaquetamos una serie tuplede valores en una sola variable usando el *operador:

    >>> *a, = 1, 2
    >>> a
    [1, 2]
    

    Para que este código funcione, el lado izquierdo de la tarea debe ser a tuple(o a list). Por eso usamos una coma al final. Esto tuplepuede contener tantas variables como necesitemos. Sin embargo, solo puede contener una expresión destacada.

    Podemos formar una expresión fija usando el operador de desempaquetado *, junto con un identificador de Python válido, al igual que *aen el código anterior. El resto de las variables del lado izquierdo tuplese llaman variables obligatorias porque deben llenarse con valores concretos, de lo contrario, obtendremos un error. Así es como funciona esto en la práctica.

    Empaquetando los valores finales en b:

    >>> a, *b = 1, 2, 3
    >>> a
    1
    >>> b
    [2, 3]
    

    Empaquetando los valores iniciales en a:

    >>> *a, b = 1, 2, 3
    >>> a
    [1, 2]
    >>> b
    3
    

    Incluir un valor en aporque by cson obligatorios:

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

    Embalaje no hay valores en a( apor defecto []), porque b, cy dson obligatorios:

    >>> *a, b, c, d = 1, 2, 3
    >>> a
    []
    >>> b
    1
    >>> c
    2
    >>> d
    3
    

    No se proporciona ningún valor para una variable obligatoria ( e), por lo que se produce un error:

    >>> *a, b, c, d, e = 1, 2, 3
     ...
    ValueError: not enough values to unpack (expected at least 4, got 3)
    

    Empaquetar valores en una variable con el *operador puede ser útil cuando necesitamos recopilar los elementos de un generador en una sola variable sin usar la list()función. En los siguientes ejemplos, usamos el *operador para empaquetar los elementos de una expresión generadora y un objeto de rango en una variable individual:

    >>> gen = (2 ** x for x in range(10))
    >>> gen
    <generator object <genexpr> at 0x7f44613ebcf0>
    >>> *g, = gen
    >>> g
    [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
    >>> ran = range(10)
    >>> *r, = ran
    >>> r
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    En estos ejemplos, el *operador empaqueta los elementos en gen, y ranen gy rrespectivamente. Con su sintaxis, evitamos la necesidad de llamar list()para crear un listvalor de un rangeobjeto, una expresión generadora o una función generadora.

    Observe que no podemos usar el operador de desempaquetado *, para empaquetar múltiples valores en una variable sin agregar una coma al final de la variable en el lado izquierdo de la asignación. Entonces, el siguiente código no funcionará:

    >>> *r = range(10)
      File "<input>", line 1
    SyntaxError: starred assignment target must be in a list or tuple
    

    Si intentamos usar el *operador para empaquetar varios valores en una sola variable, entonces necesitamos usar la tuplesintaxis singleton . Por ejemplo, para que el ejemplo anterior funcione, solo necesitamos agregar una coma después de la variable r, como en *r, = range(10).

    Uso de empaquetar y desempacar en la práctica

    Las operaciones de embalaje y desembalaje pueden resultar muy útiles en la práctica. Pueden hacer que su código sea claro, legible y Pythonico. Echemos un vistazo a algunos casos de uso comunes de empaquetado y desempaquetado en Python.

    Asignación en paralelo

    Uno de los casos de uso más comunes de descomprimir en Python es lo que podemos llamar asignación paralela. La asignación en paralelo le permite asignar los valores de forma iterable a una tuple(o list) de las variables en una declaración única y elegante.

    Por ejemplo, supongamos que tenemos una base de datos sobre los empleados de nuestra empresa y necesitamos asignar cada elemento de la lista a una variable descriptiva. Si ignoramos cómo funciona el desempaquetado iterable en Python, podemos escribir código como este:

    >>> employee = ["John Doe", "40", "Software Engineer"]
    >>> name = employee[0]
    >>> age = employee[1]
    >>> job = employee[2]
    >>> name
    'John Doe'
    >>> age
    '40'
    >>> job
    'Software Engineer'
    

    Aunque este código funciona, el manejo del índice puede ser torpe, difícil de escribir y confuso. Una solución más limpia, más legible y Pythonica se puede codificar de la siguiente manera:

    >>> name, age, job = ["John Doe", "40", "Software Engineer"]
    >>> name
    'John Doe'
    >>> age
    40
    >>> job
    'Software Engineer'
    

    Al usar el desempaquetado en Python, podemos resolver el problema del ejemplo anterior con una declaración única, sencilla y elegante. Este pequeño cambio haría que nuestro código fuera más fácil de leer y comprender para los desarrolladores recién llegados.

    Intercambio de valores entre variables

    Otra aplicación elegante de descomprimir en Python es intercambiar valores entre variables sin usar una variable temporal o auxiliar. Por ejemplo, supongamos que necesitamos intercambiar los valores de dos variables ay b. Para hacer esto, podemos ceñirnos a la solución tradicional y usar una variable temporal para almacenar el valor que se intercambiará de la siguiente manera:

    >>> a = 100
    >>> b = 200
    >>> temp = a
    >>> a = b
    >>> b = temp
    >>> a
    200
    >>> b
    100
    

    Este procedimiento consta de tres pasos y una nueva variable temporal. Si usamos el desempaquetado en Python, entonces podemos lograr el mismo resultado en un solo paso conciso:

    >>> a = 100
    >>> b = 200
    >>> a, b = b, a
    >>> a
    200
    >>> b
    100
    

    En la declaración a, b = b, a, estamos reasignando aa by bque aen una línea de código. Esto es mucho más legible y sencillo. Además, observe que con esta técnica, no hay necesidad de una nueva variable temporal.

    Recopilación de valores múltiples con *

    Cuando trabajamos con algunos algoritmos, puede haber situaciones en las que necesitemos dividir los valores de un iterable o una secuencia en fragmentos de valores para su posterior procesamiento. El siguiente ejemplo muestra cómo se utilizan las operaciones de cortelist y de división para hacerlo:

    >>> seq = [1, 2, 3, 4]
    >>> first, body, last = seq[0], seq[1:3], seq[-1]
    >>> first, body, last
    (1, [2, 3], 4)
    >>> first
    1
    >>> body
    [2, 3]
    >>> last
    4
    

    Aunque este código funciona como esperamos, lidiar con índices y porciones puede ser un poco molesto, difícil de leer y confuso para los principiantes. También tiene el inconveniente de hacer que el código sea rígido y difícil de mantener. En esta situación, el operador de desempaquetado iterable *, y su capacidad para empaquetar varios valores en una sola variable pueden ser una gran herramienta. Mira esta refactorización del código anterior:

    >>> seq = [1, 2, 3, 4]
    >>> first, *body, last = seq
    >>> first, body, last
    (1, [2, 3], 4)
    >>> first
    1
    >>> body
    [2, 3]
    >>> last
    4
    

    La línea first, *body, last = seqhace la magia aquí. El operador de desempaquetado iterable *, recopila los elementos en el medio de seqin body. Esto hace que nuestro código sea más legible, fácil de mantener y flexible. Quizás esté pensando, ¿por qué más flexible? Bueno, suponga que eso seqcambia su longitud en la carretera y aún necesita recolectar los elementos intermedios en body. En este caso, dado que estamos usando el desempaquetado en Python, no se necesitan cambios para que nuestro código funcione. Mira este ejemplo:

    >>> seq = [1, 2, 3, 4, 5, 6]
    >>> first, *body, last = seq
    >>> first, body, last
    (1, [2, 3, 4, 5], 6)
    

    Si estuviéramos usando la división de secuencias en lugar de un desempaquetado iterable en Python, entonces necesitaríamos actualizar nuestros índices y divisiones para capturar correctamente los nuevos valores.

    El uso del *operador para empaquetar varios valores en una sola variable se puede aplicar en una variedad de configuraciones, siempre que Python pueda determinar sin ambigüedades qué elemento (o elementos) asignar a cada variable. Eche un vistazo a los siguientes ejemplos:

    >>> *head, a, b = range(5)
    >>> head, a, b
    ([0, 1, 2], 3, 4)
    >>> a, *body, b = range(5)
    >>> a, body, b
    (0, [1, 2, 3], 4)
    >>> a, b, *tail = range(5)
    >>> a, b, tail
    (0, 1, [2, 3, 4])
    

    Podemos mover el *operador en el tuple(o list) de las variables para recolectar los valores de acuerdo a nuestras necesidades. La única condición es que Python pueda determinar a qué variable asignar cada valor.

    Es importante tener en cuenta que no podemos usar más de una expresión fija en la tarea. Si lo hacemos, obtendremos SyntaxErrorlo siguiente:

    >>> *a, *b = range(5)
      File "<input>", line 1
    SyntaxError: two starred expressions in assignment
    

    Si usamos dos o más *en una expresión de asignación, obtendremos un SyntaxErrormensaje que nos dirá que se encontraron expresiones con dos estrellas. Esto es así porque Python no puede determinar sin ambigüedades qué valor (o valores) queremos asignar a cada variable.

    Eliminación de valores innecesarios con *

    Otro caso de uso común del *operador es usarlo con un nombre de variable ficticia para eliminar algunos valores inútiles o innecesarios. Mira el siguiente ejemplo:

    >>> a, b, *_ = 1, 2, 0, 0, 0, 0
    >>> a
    1
    >>> b
    2
    >>> _
    [0, 0, 0, 0]
    

    Para ver un ejemplo más profundo de este caso de uso, suponga que estamos desarrollando un script que necesita determinar la versión de Python que estamos usando. Para hacer esto, podemos usar el sys.version_infoatributo . Este atributo devuelve una tupla que contiene los cinco componentes del número de versión: major, minor, micro, releaselevel, y serial. Pero solo necesitamos major, minory micropara que nuestro script funcione, para que podamos dejar el resto. He aquí un ejemplo:

    >>> import sys
    >>> sys.version_info
    sys.version_info(major=3, minor=8, micro=1, releaselevel="final", serial=0)
    >>> mayor, minor, micro, *_ = sys.version_info
    >>> mayor, minor, micro
    (3, 8, 1)
    

    Ahora, tenemos tres nuevas variables con la información que necesitamos. El resto de la información se almacena en la variable ficticia _, que nuestro programa puede ignorar. Esto puede dejar en claro a los desarrolladores recién llegados que no queremos (o necesitamos) usar la información almacenada en _porque este carácter no tiene un significado aparente.

    Nota: De forma predeterminada, el _intérprete de Python usa el carácter de subrayado para almacenar el valor resultante de las declaraciones que ejecutamos en una sesión interactiva. Entonces, en este contexto, el uso de este carácter para identificar variables ficticias puede ser ambiguo.

    Devolver tuplas en funciones

    Las funciones de Python pueden devolver varios valores separados por comas. Dado que podemos definir tupleobjetos sin usar paréntesis, este tipo de operación se puede interpretar como una devolución tuplede valores. Si codificamos una función que devuelve múltiples valores, entonces podemos realizar operaciones de empaquetado y desempaquetado iterables con los valores devueltos.

    Mira el siguiente ejemplo en el que definimos una función para calcular el cuadrado y el cubo de un número dado:

    >>> def powers(number):
    ...     return number, number ** 2, number ** 3
    ...
    >>> # Packing returned values in a tuple
    >>> result = powers(2)
    >>> result
    (2, 4, 8)
    >>> # Unpacking returned values to multiple variables
    >>> number, square, cube = powers(2)
    >>> number
    2
    >>> square
    4
    >>> cube
    8
    >>> *_, cube = powers(2)
    >>> cube
    8
    

    Si definimos una función que devuelve valores separados por comas, entonces podemos realizar cualquier operación de empaquetado o desempaquetado de estos valores.

    Fusionar iterables con el operador *

    Otro caso de uso interesante para el operador de desempaquetado *es la capacidad de combinar varios iterables en una secuencia final. Esta funcionalidad funciona para listas, tuplas y conjuntos. Eche un vistazo a los siguientes ejemplos:

    >>> my_tuple = (1, 2, 3)
    >>> (0, *my_tuple, 4)
    (0, 1, 2, 3, 4)
    >>> my_list = [1, 2, 3]
    >>> [0, *my_list, 4]
    [0, 1, 2, 3, 4]
    >>> my_set = {1, 2, 3}
    >>> {0, *my_set, 4}
    {0, 1, 2, 3, 4}
    >>> [*my_set, *my_list, *my_tuple, *range(1, 4)]
    [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
    >>> my_str = "123"
    >>> [*my_set, *my_list, *my_tuple, *range(1, 4), *my_str]
    [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, '1', '2', '3']
    

    Podemos usar el operador de desempaquetado iterable *, al definir secuencias para descomprimir los elementos de una subsecuencia (o iterable) en la secuencia final. Esto nos permitirá crear secuencias sobre la marcha de otras secuencias existentes sin llamar a métodos como append(), insert()y así sucesivamente.

    Los dos últimos ejemplos muestran que también es una forma más legible y eficiente de concatenar iterables. En lugar de escribir list(my_set) + my_list + list(my_tuple) + list(range(1, 4)) + list(my_str), simplemente escribimos [*my_set, *my_list, *my_tuple, *range(1, 4), *my_str].

    Desembalaje de diccionarios con el operador **

    En el contexto de desempaquetar en Python, el **operador se denomina operador de desempaquetado de diccionario . El uso de este operador fue ampliado por PEP 448 . Ahora, podemos usarlo en llamadas a funciones, en comprensiones y expresiones generadoras, y en pantallas .

    Un caso de uso básico para el operador de descompresión de diccionarios es fusionar varios diccionarios en un diccionario final con una sola expresión. Veamos como funciona esto:

    >>> numbers = {"one": 1, "two": 2, "three": 3}
    >>> letters = {"a": "A", "b": "B", "c": "C"}
    >>> combination = {**numbers, **letters}
    >>> combination
    {'one': 1, 'two': 2, 'three': 3, 'a': 'A', 'b': 'B', 'c': 'C'}
    

    Si usamos el operador de descompresión de diccionario dentro de una pantalla de diccionario, entonces podemos descomprimir los diccionarios y combinarlos para crear un diccionario final que incluya los pares clave-valor de los diccionarios originales, tal como hicimos en el código anterior.

    Un punto importante a tener en cuenta es que, si los diccionarios que estamos tratando de fusionar tienen claves repetidas o comunes, los valores del diccionario más a la derecha anularán los valores del diccionario más a la izquierda. He aquí un ejemplo:

    >>> letters = {"a": "A", "b": "B", "c": "C"}
    >>> vowels = {"a": "a", "e": "e", "i": "i", "o": "o", "u": "u"}
    >>> {**letters, **vowels}
    {'a': 'a', 'b': 'B', 'c': 'C', 'e': 'e', 'i': 'i', 'o': 'o', 'u': 'u'}
    

    Dado que la aclave está presente en ambos diccionarios, el valor que prevalece proviene de vowels, que es el diccionario más a la derecha. Esto sucede porque Python comienza a agregar los pares clave-valor de izquierda a derecha. Si, en el proceso, Python encuentra claves que ya salen, entonces el intérprete actualiza esas claves con el nuevo valor. Es por eso que el valor de la aclave está en minúsculas en el ejemplo anterior.

    Desembalaje en For-Loops

    También podemos usar el desempaquetado iterable en el contexto de forbucles. Cuando ejecutamos un forciclo, el ciclo asigna un elemento de su iterable a la variable objetivo en cada iteración. Si el elemento que se asignará es iterable, entonces podemos usar una tuplede las variables objetivo. El bucle descomprimirá el iterable en cuestión en las tuplevariables de destino.

    Como ejemplo, supongamos que tenemos un archivo que contiene datos sobre las ventas de una empresa de la siguiente manera:

    ProductoPrecioUnidades vendidas

    Lápiz0.251500
    Cuaderno1.30550
    Borrador0.751000

    A partir de esta tabla, podemos construir una tupla listde dos elementos. Cada uno tuplecontendrá el nombre del producto, el precio y las unidades vendidas. Con esta información queremos calcular los ingresos de cada producto. Para hacer esto, podemos usar un forciclo como este:

    >>> sales = [("Pencil", 0.22, 1500), ("Notebook", 1.30, 550), ("Eraser", 0.75, 1000)]
    >>> for item in sales:
    ...     print(f"Income for {item[0]} is: {item[1] * item[2]}")
    ...
    Income for Pencil is: 330.0
    Income for Notebook is: 715.0
    Income for Eraser is: 750.0
    

    Este código funciona como se esperaba. Sin embargo, usamos índices para acceder a elementos individuales de cada uno tuple. Esto puede ser difícil de leer y comprender para los desarrolladores recién llegados.

    Echemos un vistazo a una implementación alternativa usando el desempaquetado en Python:

    >>> for product, price, sold_units in sales:
    ...     print(f"Income for {product} is: {price * sold_units}")
    ...
    Income for Pencil is: 330.0
    Income for Notebook is: 715.0
    Income for Eraser is: 750.0
    

    Ahora estamos usando el desempaquetado iterable en nuestro forciclo. Esto hace que nuestro código sea más legible y fácil de mantener porque usamos nombres descriptivos para identificar los elementos de cada uno tuple. Este pequeño cambio permitirá que un desarrollador recién llegado comprenda rápidamente la lógica detrás del código.

    También es posible utilizar el *operador en un forbucle para empaquetar varios elementos en una sola variable de destino:

    >>> for first, *rest in [(1, 2, 3), (4, 5, 6, 7)]:
    ...     print("First:", first)
    ...     print("Rest:", rest)
    ...
    First: 1
    Rest: [2, 3]
    First: 4
    Rest: [5, 6, 7]
    

    En este forciclo, capturamos el primer elemento de cada secuencia en first. Luego, el *operador captura una listde los valores en su variable objetivo rest.

    Finalmente, la estructura de las variables objetivo debe coincidir con la estructura del iterable. De lo contrario, obtendremos un error. Eche un vistazo al siguiente ejemplo:

    >>> data = [((1, 2), 2), ((2, 3), 3)]
    >>> for (a, b), c in data:
    ...     print(a, b, c)
    ...
    1 2 2
    2 3 3
    >>> for a, b, c in data:
    ...     print(a, b, c)
    ...
    Traceback (most recent call last):
      ...
    ValueError: not enough values to unpack (expected 3, got 2)
    

    En el primer ciclo, la estructura de las variables de destino (a, b), c, coincide con la estructura de los elementos del iterable ((1, 2), 2). En este caso, el bucle funciona como se esperaba. Por el contrario, el segundo ciclo utiliza una estructura de variables de destino que no conString con la estructura de los elementos del iterable, por lo que el ciclo falla y genera un ValueError.

    Embalaje y desembalaje en funciones

    También podemos usar las funciones de empaquetado y desempaquetado de Python al definir y llamar funciones. Este es un caso de uso bastante útil y popular de empaquetar y desempacar en Python.

    En esta sección, cubriremos los conceptos básicos de cómo usar el empaquetado y desempaquetado en funciones de Python, ya sea en la definición de la función o en la llamada a la función.

    Nota: Para obtener un material más profundo y detallado sobre estos temas, consulte Argumentos de longitud variable en Python con *argsy **kwargs.

    Definición de funciones con * y **

    Podemos usar los operadores *y **en la firma de funciones de Python. Esto nos permitirá llamar a la función con un número variable de argumentos posicionales ( *) o con un número variable de argumentos de palabra clave, o ambos. Consideremos la siguiente función:

    >>> def func(required, *args, **kwargs):
    ...     print(required)
    ...     print(args)
    ...     print(kwargs)
    ...
    >>> func("Welcome to...", 1, 2, 3, site="Pharos.sh.com")
    Welcome to...
    (1, 2, 3)
    {'site': 'Pharos.sh.com'}
    

    La función anterior requiere al menos un argumento llamado required. También puede aceptar un número variable de argumentos posicionales y de palabras clave. En este caso, el *operador recopila o empaqueta argumentos posicionales adicionales en una tupla llamada argsy el **operador recopila o empaqueta argumentos de palabras clave adicionales en un diccionario llamado kwargs. Ambos, argsy kwargs, son opcionales y automáticamente predeterminados para ()y {}respectivamente.

    Aunque los nombres argsy kwargsson ampliamente utilizados por la comunidad de Python, no son un requisito para que estas técnicas funcionen. La sintaxis solo requiere *o **seguida de un identificador válido. Entonces, si puede dar nombres significativos a estos argumentos, hágalo. Eso sin duda mejorará la legibilidad de su código.

    Funciones de llamada con * y **

    Al llamar a funciones, también podemos beneficiarnos del uso del operador *y **para descomprimir colecciones de argumentos en argumentos posicionales o de palabras clave separados, respectivamente. Esta es la inversa de usar *y **en la firma de una función. En la firma, los operadores se refieren a recopilar o empaquetar un número variable de argumentos en un identificador. En la llamada, significan descomprimir un iterable en varios argumentos.

    Aquí hay un ejemplo básico de cómo funciona esto:

    >>> def func(welcome, to, site):
    ...     print(welcome, to, site)
    ...
    >>> func(*["Welcome", "to"], **{"site": 'Pharos.sh.com'})
    Welcome to Pharos.sh.com
    

    Aquí, el *operador descomprime secuencias como ["Welcome", "to"]en argumentos posicionales. De manera similar, el **operador descomprime los diccionarios en argumentos cuyos nombres coinciden con las claves del diccionario descomprimido.

    También podemos combinar esta técnica y la cubierta en la sección anterior para escribir funciones bastante flexibles. He aquí un ejemplo:

    >>> def func(required, *args, **kwargs):
    ...     print(required)
    ...     print(args)
    ...     print(kwargs)
    ...
    >>> func("Welcome to...", *(1, 2, 3), **{"site": 'Pharos.sh.com'})
    Welcome to...
    (1, 2, 3)
    {'site': 'Pharos.sh.com'}
    

    El uso de los operadores *y **, al definir y llamar funciones de Python, les dará capacidades adicionales y las hará más flexibles y poderosas.

    Conclusión

    El desempaquetado repetido resulta ser una característica bastante útil y popular en Python. Esta característica nos permite descomprimir un iterable en varias variables. Por otro lado, el embalaje consiste en la captura de varios valores en una variable utilizando el operador de desembalaje, *.

    En este tutorial, hemos aprendido cómo usar el desempaquetado iterable en Python para escribir código más legible, mantenible y Pythonico.

    Con este conocimiento, ahora podemos usar el desempaquetado iterable en Python para resolver problemas comunes como la asignación paralela y el intercambio de valores entre variables. También podemos usar esta característica de Python en otras estructuras como forbucles, llamadas a funciones y definiciones de funciones.

    Etiquetas:

    Deja una respuesta

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