O desempacotamento é o ato de extrair os elementos de uma coleção, como uma list, tuple ou dict, através da iteração.
Os valores desempacotados podem depois ser atribuídos a variáveis na mesma instrução, o que se designa habitualmente por atribuição múltipla.
Os operadores especiais * e ** são frequentemente usados em contextos de desempacotamento e com a atribuição múltipla.
*<variable_name> e **<variable_name> não devem ser confundidos com * e **. Enquanto * e ** são usados para a multiplicação e a exponenciação, respetivamente, *<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 (=) tem de corresponder ao número de valores do lado direito.
Para separar os valores, usa 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 indicados, é lançado um ValueError:
>>> x, y, z = 1, 2
ValueError: too many values to unpack (expected 3, got 2)
A atribuição múltipla não está limitada a um único tipo de dados:
>>> 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.
Esta 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 as tuples são imutáveis, não podes trocar elementos numa tuple.
Os exemplos abaixo usam lists, mas os mesmos conceitos aplicam-se a tuples.
Em Python, é possível desempacotar os elementos de list/tuple/dictionary em variáveis distintas.
Como os valores aparecem dentro de lists/tuples numa ordem específica, são desempacotados para as variáveis pela mesma ordem:
>>> fruits = ["apple", "banana", "cherry"]
>>> x, y, z = fruits
>>> x
"apple"
Se houver valores que não são necessários, podes usar _ para os assinalar:
>>> fruits = ["apple", "banana", "cherry"]
>>> _, _, z = fruits
>>> z
"cherry"
O desempacotamento e a atribuição de valores de uma list/tuple contida dentro de uma list ou tuple (também conhecidas como listas/tuplas aninhadas) funciona da mesma forma que um desempacotamento superficial, mas muitas vezes precisa de qualificadores para clarificar o contexto ou a posição:
>>> fruits_vegetables = [["apple", "banana"], ["carrot", "potato"]]
>>> [[a, b], [c, d]] = fruits_vegetables
>>> a
"apple"
>>> d
"potato"
Também podes 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, vais obter 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, podes usar o operador * para capturar os valores restantes.
Isto é mais claro do que cortar a list/tuple em fatias (o que, em algumas situações, é menos legível).
Por exemplo, o primeiro elemento pode ser extraído e os restantes valores 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 no início e no 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 * 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 por omissão as chaves.
Por isso, ao desempacotar um dict, só podes desempacotar as chaves e não os valores:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory
>>> x
"apple"
Se quiseres desempacotar os valores, podes usar o método <dict>.values():
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.values()
>>> x
6
Se precisares tanto das chaves como dos valores, usa o método <dict>.items().
O <dict>.items() gera uma vista iterável que contém pares chave-valor.
Estes podem ser desempacotados numa tuple:
>>> fruits_inventory = {"apple": 6, "banana": 2, "cherry": 3}
>>> x, y, z = fruits_inventory.items()
>>> x
("apple", 6)
O empacotamento é a capacidade de agrupar vários valores numa única list atribuída a uma variável.
Isto é útil quando queres desempacotar valores, fazer alterações e depois empacotar os resultados de novo numa variável.
Também torna possível fazer fusões de 2 ou mais lists/tuples/dicts.
*
Empacotar uma list/tuple pode ser feito com o operador *.
Isto empacota 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 saberes mais sobre a utilização de * do lado esquerdo, vê a PEP 3132.
**
Empacotar um dicionário faz-se com o operador **.
Isto empacota todos os pares chave-valor de um dicionário noutro dicionário, 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 crias uma função que aceita um número arbitrário de argumentos, podes usar *args ou **kwargs na definição da função.
O *args é usado para empacotar um número arbitrário de argumentos posicionais (não nomeados) como uma tuple e
o **kwargs é usado para empacotar um número arbitrário de argumentos nomeados como um dicionário.
Utilização 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")
Utilização 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}
O *args e o **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
Também podes escrever parâmetros antes de *args para permitir argumentos posicionais específicos.
Os argumentos nomeados individuais têm então de aparecer antes de **kwargs.
Os argumentos têm de ser estruturados assim:
def my_function(<positional_args>, *args, <key-word_args>, **kwargs)
Se não seguires esta ordem, vais obter 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 resulta num 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'
Podes usar * para desempacotar uma list/tuple de argumentos numa chamada de função.
Isto é 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 * juntamente com a função incorporada zip() é outro caso de uso comum.
A função zip() recebe vários iteráveis e devolve 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)]
O teu amigo Linus é maquinista de comboios de mercadorias que circulam entre cidades. Embora seja fantástico a conduzir comboios, não é tão bom a lidar com logística ou computadores. Gostaria de contar com a tua ajuda em programação para organizar os detalhes dos comboios e corrigir erros nos dados das rotas.
Este exercício poderia ser facilmente resolvido com fatiamento, indexação e vários métodos de dict.
No entanto, gostaríamos que praticasses empacotamento, desempacotamento e atribuição múltipla ao resolveres cada uma das tarefas abaixo.
O teu amigo tem andado a registar o identificador (ID) de cada vagão, mas nunca sabe quantos vagões o sistema vai ter de processar em cada momento. Seria muito mais fácil para o resto do programa de logística ter estes dados agrupados num único list.
Implementa 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 depois return os IDs recebidos como uma única list.
>>> get_list_of_wagons(1, 7, 12, 3, 14, 8, 5)
[1, 7, 12, 3, 14, 8, 5]
A esta altura, já estás a começar a perceber os dados e a forma como são usados no programa de logística. O sistema de IDs atribui sempre à locomotiva o ID 1, e aos restantes vagões do comboio um ID escolhido aleatoriamente maior do que 1.
O teu amigo teve de ligar dois vagões novos ao comboio e esqueceu-se de atualizar o sistema!
Agora, os dois primeiros vagões da list do comboio têm de ser movidos para o fim, ou tudo ficará fora de ordem.
Para complicar mais as coisas, o teu amigo acabou de descobrir uma segunda list que parece conter IDs de vagões em falta.
Do que se lembra é que, depois de os vagões novos serem movidos, os IDs desta segunda list devem ser colocados imediatamente a seguir à locomotiva designada.
O Linus ficaria muito grato se corrigisses os seus erros e consolidasses os dados.
Implementa uma função fix_list_of_wagons() que recebe duas lists com IDs de vagões.
Deve reposicionar os dois primeiros itens da primeira list para o fim e inserir os valores da segunda list atrás (do lado direito) do ID da locomotiva (1).
A função deve depois 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, o Linus gostaria que atualizasses as informações de encaminhamento do sistema.
Ao longo de uma rota de transporte, um comboio pode fazer paragens em algumas estações diferentes para recolher e/ou deixar carga.
Cada viagem pode ter um número diferente destes pontos de entrega intermédios.
O teu amigo gostaria que atualizasses o dict de encaminhamento do sistema com quaisquer informações de entrega em falta ou adicionais.
Implementa uma função add_missing_stops() que aceita um dict de encaminhamento seguido de um número variável de argumentos de palavra-chave.
Estes argumentos podem ter a forma de um dict com uma ou mais paragens, ou de qualquer número de pares de palavra-chave stop_number=city.
A tua função deve depois devolver o dict de encaminhamento atualizado com uma key adicional que contém uma list com todas as paragens adicionadas, por 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"]}
O Linus tem andado a trabalhar no programa de encaminhamento e reparou que faltam alguns detalhes importantes em certas rotas.
As informações iniciais da rota foram construídas como um dict e o teu amigo gostaria que atualizasses esse dict com o que estiver em falta.
Cada rota do sistema exige detalhes ligeiramente diferentes, por isso o Linus preferiria mesmo uma solução genérica.
Implementa uma função chamada extend_route_information() que aceita dois dicts.
O primeiro dict contém as cidades de origem e de destino entre as quais circula a rota do comboio.
O segundo dict contém outros detalhes de encaminhamento, como a velocidade, o comprimento ou a temperatura do comboio.
A função deve devolver um dict consolidado com todas as informações de encaminhamento.
O segundo dict pode conter propriedades diferentes ou mais 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 o Linus estava a inspecionar o depósito de vagões, reparou que os vagões não estavam a ser guardados 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 guardados no depósito em grelhas, em que cada coluna da grelha tem vagões da mesma cor.
No entanto, o sistema de logística mostra que as lists de vagões a guardar no depósito têm as suas linhas agrupadas por cor.
Mas para a grelha de armazenamento funcionar corretamente, cada linha deve ter três cores diferentes, para que as colunas se alinhem por cor.
O teu amigo gostaria que organizasses as lists do depósito de vagões, para que os vagões sejam guardados corretamente.
Implementa 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>).
A tua função deve devolver uma list com as três lists de "linha" reordenadas, de modo a colocar os vagões 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")]
]
Inscreve-te no Exercism para aprenderes e dominares Python com 17 conceitos146 exercícios, e mentoria humana real, tudo grátis.