Desempacotamento é o ato de extrair os elementos de uma coleção, como uma list, uma tuple ou um dict, usando iteração.
Os valores desempacotados podem então ser atribuídos a variáveis na mesma instrução.
Um exemplo muito comum desse comportamento é for item in list, em que item assume o valor de cada elemento da list, um de cada vez, ao longo da iteração.
A atribuição múltipla é a capacidade de atribuir várias variáveis a valores desempacotados em uma única instrução.
Isso deixa o código mais conciso e legível, e é feito separando as variáveis a serem atribuídas com uma vírgula, como em first, second, third = (1,2,3) ou for index, item in enumerate(iterable).
Os operadores especiais * e ** são usados com frequência em contextos de desempacotamento.
O * pode ser usado para combinar várias lists/tuples em uma única list/tuple, desempacotando cada uma em uma nova list/tuple comum.
O ** pode ser usado para combinar vários dicionários em um único dicionário, desempacotando cada um em um novo dict comum.
Quando o operador * é usado sem uma coleção, ele empacota uma série de valores em uma list.
Isso é usado com frequência na atribuição múltipla para agrupar em uma única variável todos os elementos "restantes" que não têm atribuição individual.
Também é comum em Python aproveitar esse comportamento de desempacotamento/empacotamento ao usar ou definir funções que recebem um número arbitrário de argumentos posicionais ou nomeados.
Você verá com frequência esses parâmetros "especiais" definidos como def some_function(*args, **kwargs) e os argumentos "especiais" usados como some_function(*some_tuple, **some_dict).
Não confunda *<variable_name> e **<variable_name> com * e **. Enquanto * e ** são usados para multiplicação e exponenciação, respectivamente, *<variable_name> e **<variable_name> são usados como operadores de empacotamento e desempacotamento.
Na atribuição múltipla, o número de variáveis do lado esquerdo do operador de atribuição (=) deve ser igual ao número de valores do lado direito.
Para separar os valores, use uma vírgula ,:
>>> a, b = 1, 2
>>> a
1
Se a atribuição múltipla receber um número incorreto de variáveis para os valores fornecidos, um ValueError será lançado:
>>> x, y, z = 1, 2
ValueError: too many values to unpack (expected 3, got 2)
A atribuição múltipla não se limita a um único tipo de dado:
>>> x, y, z = 1, "Hello", True
>>> x
1
>>> y
'Hello'
>>> z
True
A atribuição múltipla pode ser usada para trocar elementos em lists.
Essa prática é bastante comum em algoritmos de ordenação.
Por exemplo:
>>> numbers = [1, 2]
>>> numbers[0], numbers[1] = numbers[1], numbers[0]
>>> numbers
[2, 1]
Como tuples são imutáveis, você não pode trocar elementos em uma tuple.
Os exemplos abaixo usam lists, mas os mesmos conceitos se aplicam a tuples.
Em Python, é possível desempacotar os elementos de uma list/tuple/dict em variáveis distintas.
Como os valores aparecem em lists/tuples em uma ordem específica, eles são desempacotados nas variáveis na mesma ordem:
>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"
Se houver valores que não são necessários, você pode usar _ para marcá-los:
>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"
Desempacotar e atribuir valores de uma list/tuple dentro de uma list ou tuple (também conhecidas como list/tuples aninhadas) funciona da mesma forma que um desempacotamento raso, mas muitas vezes precisa de qualificadores para deixar claro o contexto ou a posição do valor:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"
>>> d
"potato"
Você também pode desempacotar profundamente apenas uma parte de uma list/tuple aninhada:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [a, [c, d]] = fruits_vegetables
>>> a
["apple", "banana"]
>>> c
"carrot"
Se o desempacotamento tiver variáveis em posições incorretas e/ou um número incorreto de valores, você receberá um ValueError:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [d]] = fruits_vegetables
ValueError: too many values to unpack (expected 1)
list/tuple com *
Ao desempacotar uma list/tuple, você pode usar o operador * para capturar os valores "restantes".
Isso é mais claro do que fatiar a list/tuple (o que, em algumas situações, é menos legível).
Por exemplo, podemos extrair o primeiro elemento e empacotar os valores restantes em uma nova list sem o primeiro elemento:
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *last = fruits
>>> x
"apple"
>>> last
["banana", "cherry", "orange", "kiwi", "melon", "mango"]
Também podemos extrair os valores do início e do fim da list, agrupando todos os valores do meio:
>>> fruits = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> x, *middle, y, z = fruits
>>> y
"melon"
>>> middle
["banana", "cherry", "orange", "kiwi"]
Também podemos usar o * no desempacotamento profundo:
>>> fruits_vegetables = [["apple", "banana", "melon"], ["carrot", "potato", "tomato"]]
>>> [[a, *rest], b] = fruits_vegetables
>>> a
"apple"
>>> rest
["banana", "melon"]
Desempacotar um dicionário é um pouco diferente de desempacotar uma list/tuple.
A iteração sobre dicionários usa as chaves por padrão.
Então, ao desempacotar um dict, você só pode desempacotar as chaves, e não os valores:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"
Se você quiser desempacotar os valores, pode usar o método <dict>.values():
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6
Se você precisar tanto das chaves quanto dos valores, use o método <dict>.items().
<dict>.items() gera uma visão iterável contendo pares chave-valor.
Eles podem ser desempacotados em uma tuple:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)
Empacotamento é a capacidade de agrupar vários valores em uma única list atribuída a uma variável.
Isso é útil quando você quer desempacotar valores, fazer alterações e depois empacotar os resultados de volta em uma variável.
Também torna possível mesclar 2 ou mais lists/tuples/dicts.
list/tuple com *
Empacotar uma list/tuple pode ser feito com o operador *.
Isso empacota todos os valores em uma list/tuple.
>>> fruits = ("apple", "banana", "cherry")
>>> more_fruits = ["orange", "kiwi", "melon", "mango"]
# fruits and more_fruits are unpacked and then their elements are packed into combined_fruits
>>> combined_fruits = *fruits, *more_fruits
# If there is no * on to the left of the "=" the result is a tuple
>>> combined_fruits
("apple", "banana", "cherry", "orange", "kiwi", "melon", "mango")
# If the * operator is used on the left side of "=" the result is a list.
# Note the trailing comma.
>>> *combined_fruits_too, = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
# A list literal can be used instead, but might not be as readable.
>>> [*combined_fruits_too] = *fruits, *more_fruits
>>> combined_fruits_too
['apple', 'banana', 'cherry', 'orange', 'kiwi', 'melon', 'mango']
Para mais detalhes sobre o uso de * e **, consulte a PEP 3132 e a PEP 448.
**
Empacotar um dicionário é feito com o operador **.
Isso empacota todos os pares chave-valor de um dicionário em outro, ou combina dois dicionários.
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> more_fruits_inventory = {"orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
# fruits_inventory and more_fruits_inventory are unpacked into key-values pairs and combined.
>>> combined_fruits_inventory = {**fruits_inventory, **more_fruits_inventory}
# then the pairs are packed into combined_fruits_inventory
>>> combined_fruits_inventory
{"apple": 6, "banana": 2, "cherry": 3, "orange": 4, "kiwi": 1, "melon": 2, "mango": 3}
* e ** com funçõesQuando você cria uma função que aceita um número arbitrário de argumentos, pode usar *args ou **kwargs na definição da função.
*args empacota um número arbitrário de argumentos posicionais (não nomeados) e
**kwargs empacota um número arbitrário de argumentos nomeados.
Uso de *args:
# This function is defined to take any number of positional arguments
>>> def my_function(*args):
... print(args)
# Arguments given to the function are packed into a tuple
>>> my_function(1, 2, 3)
(1, 2, 3)
>>> my_function("Hello")
("Hello")
>>> my_function(1, 2, 3, "Hello", "Mars")
(1, 2, 3, "Hello", "Mars")
Uso de **kwargs:
# This function is defined to take any number of keyword arguments
>>> def my_function(**kwargs):
... print(kwargs)
# Arguments given to the function are packed into a dictionary
>>> my_function(a=1, b=2, c=3)
{"a": 1, "b": 2, "c": 3}
*args e **kwargs também podem ser usados em conjunto:
>>> def my_function(*args, **kwargs):
... print(sum(args))
... for key, value in kwargs.items():
... print(str(key) + " = " + str(value))
>>> my_function(1, 2, 3, a=1, b=2, c=3)
6
a = 1
b = 2
c = 3
Você também pode escrever parâmetros antes de *args para permitir argumentos posicionais específicos.
Os argumentos nomeados individuais, então, precisam aparecer antes de **kwargs.
Os argumentos precisam ser estruturados assim:
def my_function(<positional_args>, *args, <key-word_args>, **kwargs)
Se você não seguir essa ordem, receberá um erro.
>>> def my_function(a, b, *args):
... print(a)
... print(b)
... print(args)
>>> my_function(1, 2, 3, 4, 5)
1
2
(3, 4, 5)
Escrever os argumentos em uma ordem incorreta resultará em um erro:
>>> def my_function(*args, a, b):
... print(args)
>>>my_function(1, 2, 3, 4, 5)
Traceback (most recent call last):
File "c:\something.py", line 3, in <module>
my_function(1, 2, 3, 4, 5)
TypeError: my_function() missing 2 required keyword-only arguments: 'a' and 'b'
Você pode usar o * para desempacotar uma list/tuple de argumentos em uma chamada de função.
Isso é muito útil para funções que não aceitam um iterable:
>>> def my_function(a, b, c):
... print(c)
... print(b)
... print(a)
numbers = [1, 2, 3]
>>> my_function(*numbers)
3
2
1
Usar o desempacotamento com * junto com a função embutida zip() é outro caso de uso comum.
A função zip() recebe vários iteráveis e retorna uma list de tuples com os valores de cada iterable agrupados:
>>> values = (['x', 'y', 'z'], [1, 2, 3], [True, False, True])
>>> a, *rest = zip(*values)
>>> rest
[('y', 2, False), ('z', 3, True)]