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 *