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, o que costuma ser chamado de atribuição múltipla.
Os operadores especiais * e ** são usados com frequência em contextos de desempacotamento e com atribuição múltipla.
*<variable_name> e **<variable_name> não devem ser confundidos 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 (=) precisa 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 é bem 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.
No Python, é possível desempacotar os elementos de list/tuple/dictionary em variáveis distintas.
Como os valores aparecem em lists/tuples numa 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 sinalizá-los:
>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"
Desempacotar e atribuir valores de uma list/tuple contida dentro de uma list ou tuple (também conhecidas como lists/tuples aninhadas) funciona do mesmo jeito que um desempacotamento raso, mas muitas vezes precisa de qualificadores para deixar claro o contexto ou a posição:
>>> 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 com posicionamento incorreto 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)
*
Ao desempacotar uma list/tuple você pode usar o operador * para capturar os valores que sobraram.
Isso é mais claro do que fatiar a list/tuple (o que, em algumas situações, é menos legível).
Por exemplo, o primeiro elemento pode ser extraído e os valores restantes podem ser colocados numa 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 enquanto agrupamos 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 * 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.
Esses pares podem ser desempacotados numa 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 numa única list que é atribuída a uma variável.
Isso é útil quando você quer desempacotar valores, fazer alterações e depois empacotar os resultados de volta numa variável.
Também torna possível fazer mesclagens de 2 ou mais lists/tuples/dicts.
*
Empacotar uma list/tuple pode ser feito com o operador *.
Isso vai empacotar todos os valores numa 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']
Para saber mais sobre o uso de * no lado esquerdo, veja a PEP 3132.
**
Empacotar um dicionário é feito com o operador **.
Isso vai empacotar todos os pares chave-valor de um dicionário em outro dicionário, ou combinar 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 é usado para empacotar um número arbitrário de argumentos posicionais (não nomeados) numa tuple e
**kwargs é usado para empacotar um número arbitrário de argumentos nomeados num dicionário.
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.
Argumentos nomeados individuais, então, precisam vir 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, vai 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 numa ordem incorreta vai 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 * para desempacotar uma list/tuple de argumentos numa 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)]
Seu amigo Linus é maquinista e conduz trens de carga entre cidades. Apesar de ser incrível no comando dos trens, ele não é tão bom assim com logística ou computadores. Ele gostaria de contar com a sua ajuda em programação para organizar os detalhes dos trens e corrigir erros nos dados das rotas.
Este exercício poderia ser resolvido facilmente usando fatiamento, indexação e vários métodos de dict.
No entanto, queremos que você pratique empacotamento, desempacotamento e atribuição múltipla ao resolver cada uma das tarefas abaixo.
Seu amigo vem acompanhando o identificador (ID) de cada vagão, mas nunca tem certeza de quantos vagões o sistema vai precisar processar em um dado momento. Seria muito mais fácil para o restante do programa de logística ter esses dados empacotados em uma única list.
Implemente uma função get_list_of_wagons() que aceita um número arbitrário de IDs de vagões.
Cada ID será um número inteiro positivo.
A função deve então return os IDs fornecidos como uma única list.
>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
[1, 7, 12, 3, 14, 8, 5]
Neste ponto, você já está começando a entender os dados e como eles são usados no programa de logística. O sistema de IDs sempre atribui à locomotiva o ID 1, e os demais vagões do trem recebem um ID escolhido aleatoriamente maior que 1.
Seu amigo teve que conectar dois vagões novos ao trem e esqueceu de atualizar o sistema!
Agora, os dois primeiros vagões da list do trem precisam ser movidos para o final, ou tudo vai ficar fora de ordem.
Para complicar ainda mais, seu amigo acabou de encontrar uma segunda list que parece conter IDs de vagões que estavam faltando.
Tudo o que ele lembra é que, depois que os novos vagões forem movidos, os IDs dessa segunda list devem ser colocados logo depois da locomotiva indicada.
Linus ficaria muito grato se você corrigisse os erros dele e consolidasse os dados.
Implemente uma função fix_list_of_wagons() que recebe duas lists contendo IDs de vagões.
Ela deve reposicionar os dois primeiros itens da primeira list para o final e inserir os valores da segunda list atrás (à direita) do ID da locomotiva (1).
A função deve então return uma list com as modificações.
>>> fix_list_of_wagons([2, 5, 1, 7, 4, 12, 6, 3, 13], [3, 17, 6, 15])
[1, 3, 17, 6, 15, 7, 4, 12, 6, 3, 13, 2, 5]
Agora que todos os dados dos vagões estão corretos, Linus gostaria que você atualizasse as informações de roteamento do sistema.
Ao longo de uma rota de transporte, um trem pode fazer paradas em algumas estações diferentes para pegar e/ou deixar carga.
Cada viagem pode ter um número diferente desses pontos de entrega intermediários.
Seu amigo gostaria que você atualizasse o dict de roteamento do sistema com qualquer informação de entrega que esteja faltando ou que seja adicional.
Implemente uma função add_missing_stops() que aceita um dict de roteamento seguido de um número variável de argumentos nomeados.
Esses argumentos podem estar na forma de um dict contendo uma ou mais paradas, ou de qualquer número de pares nomeados stop_number=city.
Sua função deve então retornar o dict de roteamento atualizado com uma key adicional que contém uma list de todas as paradas adicionadas, na ordem.
>>> add_missing_stops({"from": "New York", "to": "Miami"},
stop_1="Washington, DC", stop_2="Charlotte", stop_3="Atlanta",
stop_4="Jacksonville", stop_5="Orlando")
{"from": "New York", "to": "Miami", "stops": ["Washington, DC", "Charlotte", "Atlanta", "Jacksonville", "Orlando"]}
Linus vem trabalhando no programa de roteamento e percebeu que certas rotas estão sem alguns detalhes importantes.
As informações iniciais da rota foram montadas como um dict e seu amigo gostaria que você atualizasse esse dict com o que estiver faltando.
Cada rota do sistema exige detalhes um pouco diferentes, então Linus preferiria muito uma solução genérica.
Implemente uma função chamada extend_route_information() que aceita dois dicts.
O primeiro dict contém as cidades de origem e destino entre as quais o trem circula.
O segundo dict contém outros detalhes de roteamento, como velocidade, comprimento ou temperatura do trem.
A função deve retornar um dict consolidado com todas as informações de roteamento.
O segundo dict pode conter propriedades diferentes ou em maior número do que as mostradas no exemplo.
>>> extend_route_information({"from": "Berlin", "to": "Hamburg"}, {"length": "100", "speed": "50"})
{"from": "Berlin", "to": "Hamburg", "length": "100", "speed": "50"}
Quando Linus estava inspecionando o depósito de vagões, ele notou que os vagões não estavam sendo armazenados na ordem correta. Além de um ID, cada vagão tem uma cor que corresponde ao tipo de carga que transporta. Os vagões são armazenados no depósito em grades, onde cada coluna da grade tem vagões da mesma cor.
No entanto, o sistema de logística mostra que as lists de vagões a serem armazenados no depósito têm as suas linhas agrupadas por cor.
Mas, para que a grade de armazenamento funcione corretamente, cada linha deve ter três cores diferentes, para que as colunas se alinhem por cor.
Seu amigo gostaria que você organizasse as lists do depósito de vagões, para que os vagões sejam armazenados corretamente.
Implemente uma função chamada fix_wagon_depot() que aceita uma list de três itens.
Cada item da list é uma sublista (ou "linha") que contém três tuples.
Cada tuple é um par (<wagon ID>, <wagon color>).
Sua função deve retornar uma list com as três lists "linha" reordenadas para que os vagões sejam colocados nas posições corretas.
>>> fix_wagon_depot([
[(2, "red"), (4, "red"), (8, "red")],
[(5, "blue"), (9, "blue"), (13,"blue")],
[(3, "orange"), (7, "orange"), (11, "orange")],
])
[
[(2, "red"), (5, "blue"), (3, "orange")],
[(4, "red"), (9, "blue"), (7, "orange")],
[(8, "red"), (13,"blue"), (11, "orange")]
]
Crie sua conta no Exercism para aprender e dominar Python com 17 conceitos146 exercícios e mentoria humana de verdade, tudo de graça.