Como usar os operadores de descompactação (*, **) em Python?

Python é a linguagem de programação mais usada. Hoje você aprenderá a usar um de seus principais — mas frequentemente ignorados — recursos, desempacotando em Python.

Você provavelmente já viu * e ** no código de outras pessoas ou até mesmo os usou sem realmente saber qual é o seu propósito. Estaremos percorrendo o conceito de descompactação e como usá-lo para escrever mais código Pythonic.

Aqui está uma lista de conceitos que você achará útil ao ler este tutorial:

  • Iterável: Qualquer sequência que pode ser iterada por um loop for, como conjuntos, listas, tuplas e dicionários
  • Callable: Um objeto Python que pode ser chamado usando parênteses duplos (), por exemplo, myfunction ()
  • Shell: ambiente de tempo de execução interativo que nos permite executar o código Python. Podemos chamá-lo executando “python” em um terminal
  • Variável: Nome simbólico que armazena um objeto e tem um local de memória reservado.

Vamos começar com a confusão mais frequente: asterísticos em Python também são operadores aritméticos. um asterisco

é usado para multiplicação, enquanto dois deles (**) referem-se à exponenciação.

>>> 3*3
9
>>> 3**3
27

Podemos provar isso abrindo um shell Python e digitando:

Observação: você precisa ter o Python 3 instalado para acompanhar este tutorial. Se você não o tiver instalado, confira nosso guia de instalação do Python.

Como você pode ver, estamos usando o asterisco após o primeiro número e antes do segundo. Quando você vê isso, significa que estamos usando os operadores aritméticos.

>>> *range(1, 6),
(1, 2, 3, 4, 5)
>>> {**{'vanilla':3, 'chocolate':2}, 'strawberry':2}
{'vanilla': 3, 'chocolate': 2, 'strawberry': 2}

Por outro lado, usamos os asteriscos (*, **) antes de um iterável para descompactá-lo — por exemplo:

Não se preocupe se você não entender, este é apenas um preâmbulo para desempacotar em Python. Então vá em frente e leia todo o tutorial!

O que está desempacotando?

  Como baixar drivers NVIDIA sem experiência GeForce

Desempacotar é o processo de extrair coisas — iteráveis ​​como listas, tuplas e dicionários. Pense nisso como abrir uma caixa e retirar diferentes itens como cabos, fones de ouvido ou um USB.

Desempacotar em Python é semelhante a desempacotar uma caixa na vida real.

>>> mybox = ['cables', 'headphones', 'USB']
>>> item1, item2, item3 = mybox

Vamos traduzir este mesmo exemplo em código para um melhor entendimento:

Como você pode ver, estamos atribuindo os três itens dentro da lista mybox a três variáveis ​​item1, item2, item2. Esse tipo de atribuição de variável é o conceito fundamental de desempacotamento em Python.

>>> item1
'cables'
>>> item2
'headphones'
>>> item3
'USB'

Se você tentar obter o valor de cada item, notará que item1 refere-se a “cabos”, item2 refere-se a “fones de ouvido” e assim por diante.

>>> newbox = ['cables', 'headphones', 'USB', 'mouse']
>>> item1, item2, item3 = newbox
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 3)

Até aqui, tudo parecia estar bem com este código, mas e se quiséssemos desempacotar uma lista com mais elementos — mantendo a mesma quantidade de variáveis ​​atribuídas?

Provavelmente você esperava esse tipo de erro. Essencialmente, estamos atribuindo 4 itens de lista a três variáveis, como o Python consegue atribuir os valores corretos? Não, é porque temos um ValueError

com a mensagem “muitos valores para desempacotar”. Isso está acontecendo porque estamos definindo três variáveis ​​à esquerda e quatro valores (correspondentes à lista newbox) à direita.

>>> lastbox = ['cables', 'headphones']
>>> item1, item2, item3 = lastbox
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 2)

Se você tentar fazer um processo semelhante, mas com mais variáveis ​​do que valores para descompactar, obterá outro ValueError, exceto que com uma mensagem ligeiramente diferente:

Nota: Temos trabalhado com listas, mas você pode usar esta forma de desempacotar com qualquer iterável (listas, conjuntos, tuplas, dicionários)

Então, como vamos superar esta situação? Existe alguma maneira de descompactar todos os itens de um iterável para algumas variáveis ​​sem obter erros?

Claro que existe, e é chamado de operador de descompactação ou operador de asterisco (*, **). Vamos ver como usá-lo em Python.

Como descompactar listas com o operador *

O operador asterisco

>>> first, *unused, last = [1, 2, 3, 5, 7]
>>> first
1
>>> last
7
>>> unused
[2, 3, 5]

é usado para descompactar todos os valores de um iterável que ainda não foram atribuídos.

>>> first, *_, last = [1, 2, 3, 5, 7]
>>> _
[2, 3, 5]

Vamos supor que você queira obter o primeiro e o último elemento de uma lista sem usar índices, poderíamos fazer isso com o operador asterisco:

>>> first, *_, last = [1, 2]
>>> first
1
>>> last
2
>>> _
[]

Como você pode perceber, obtemos todos os valores não utilizados com o operador asterisco. A maneira preferida de descartar valores é usar uma variável de sublinhado (_), que às vezes é usada como uma “variável fictícia”.

  Tudo o que você precisa saber sobre o Data Fabric para empresas digitais

Ainda podemos usar esse truque mesmo que a lista tenha apenas dois elementos:

Nesse caso, a variável underscore (variável dummy) armazena uma lista vazia para que as outras duas variáveis ​​ao seu redor possam acessar os valores disponíveis da lista.

>>> *string = 'PythonIsTheBest'

Solução de problemas comuns

>>> *string = 'PythonIsTheBest'
  File "<stdin>", line 1
SyntaxError: starred assignment target must be in a list or tuple

Podemos desempacotar um elemento único de um iterável. Por exemplo, você criaria algo assim: No entanto, o código acima retornará um SyntaxError:Isso porque segundo o

especificação PEP

:

>>> *string, = 'PythonIsTheBest'
>>> string
['P', 'y', 't', 'h', 'o', 'n', 'I', 's', 'T', 'h', 'e', 'B', 'e', 's', 't']

Uma tupla (ou lista) no lado esquerdo de uma atribuição simples

>>> *numbers, = range(5)
>>> numbers
[0, 1, 2, 3, 4]

Se quisermos descompactar todos os valores de um iterável em uma única variável, devemos configurar uma tupla, portanto, adicionar uma simples vírgula será suficiente:

Outro exemplo seria usar a função range, que retorna uma sequência de números.

Agora que você sabe como desempacotar listas e tuplas com um asterisco, é hora de desempacotar dicionários.

Como descompactar dicionários com o operador **

>>> **greetings, = {'hello': 'HELLO', 'bye':'BYE'} 
...
SyntaxError: invalid syntax

Enquanto um único asterisco é usado para desempacotar listas e tuplas, o asterisco duplo (**) é usado para desempacotar dicionários.

>>> food = {'fish':3, 'meat':5, 'pasta':9} 
>>> colors = {'red': 'intensity', 'yellow':'happiness'}
>>> merged_dict = {**food, **colors}
>>> merged_dict
{'fish': 3, 'meat': 5, 'pasta': 9, 'red': 'intensity', 'yellow': 'happiness'}

Infelizmente, não podemos desempacotar um dicionário para uma única variável como temos feito com tuplas e listas. Isso significa que o seguinte gerará um erro:

No entanto, podemos usar o operador ** dentro de callables e outros dicionários. Por exemplo, se quisermos criar um dicionário mesclado, feito a partir de outros dicionários, poderíamos utilizar o código abaixo:

Esta é uma maneira bastante curta de criar dicionários compostos, no entanto, esta não é a principal abordagem de descompactação em Python.

Vamos ver como podemos usar unpacking com callables

Empacotamento em funções: args e kwargs

Você provavelmente já viu args e kwargs antes implementados em classes ou funções. Vamos ver por que precisamos usá-los junto com callables.

>>> def product(n1, n2):
...     return n1 * n2
... 
>>> numbers = [12, 1]
>>> product(*numbers)
12

Empacotando com o operador * (args)

>>> product(12, 1)
12

Suponha que temos uma função que calcula o produto de dois números.

>>> numbers = [12, 1, 3, 4]
>>> product(*numbers)
...
TypeError: product() takes 2 positional arguments but 4 were given

Como você pode ver, estamos desempacotando os números da lista para a função, então, na verdade, estamos executando o seguinte:

>>> def product(*args):
...     result = 1
...     for i in args:
...             result *= i
...     return result
...
>>> product(*numbers)
144

Até aqui, tudo funciona muito bem, mas e se quiséssemos passar uma lista mais longa? Certamente gerará um erro porque a função está recebendo mais argumentos do que é capaz de gerenciar.

  3 razões para usar o periscópio de transmissão de vídeo ao vivo

Podemos resolver tudo isso empacotando a lista diretamente na função, o que cria um iterável dentro dela e nos permite passar qualquer quantidade de argumentos para a função.

Aqui estamos tratando o parâmetro args como iterável, percorrendo seus elementos e retornando o produto de todos os números. Observe como o número inicial do resultado deve ser um porque se começarmos com zero, a função sempre retornará zero. Nota: args é apenas uma convenção, você pode usar qualquer outro nome de parâmetroTambém poderíamos passar números arbitrários para a função sem usar uma lista, assim como com o built-in

>>> product(5, 5, 5)
125
>>> print(5, 5, 5)
5 5 5

função de impressão

>>> def test_type(*args):
...     print(type(args))
...     print(args)
... 
>>> test_type(1, 2, 4, 'a string')
<class 'tuple'>
(1, 2, 4, 'a string')

.

Por fim, vamos obter o tipo de objeto dos argumentos de uma função.

Conforme observado no código acima, o tipo de args será sempre tupla e o conteúdo dela será todos os argumentos sem palavras-chave passados ​​para a função.

Embalagem com o operador ** (kwargs)

>>> def make_person(name, **kwargs):
...     result = name + ': '
...     for key, value in kwargs.items():
...             result += f'{key} = {value}, '
...     return result
... 
>>> make_person('Melissa', id=12112, location='london', net_worth=12000)
'Melissa: id = 12112, location = london, net_worth = 12000, '

Como vimos anteriormente, o operador ** é usado exclusivamente para dicionários. Isso significa que com esse operador podemos passar pares chave-valor para a função como um parâmetro.

Vamos criar uma função make_person, que recebe um argumento posicional “name” e uma quantidade indefinida de argumentos com palavras-chave.

Como você pode ver, a instrução **kwargs converte todos os argumentos com palavras-chave em um dicionário, que podemos iterar dentro da função.

>>> def test_kwargs(**kwargs):
...     print(type(kwargs))
...     print(kwargs)
... 
>>> test_kwargs(random=12, parameters=21)
<class 'dict'>
{'random': 12, 'parameters': 21}

Nota: kwargs é apenas uma convenção, você pode nomear este parâmetro como quiser

Podemos verificar o tipo dos kwargs da mesma forma que fizemos com args:

>>> def my_final_function(*args, **kwargs):
...     print('Type args: ', type(args))
...     print('args: ', args)
...     print('Type kwargs: ', type(kwargs))
...     print('kwargs: ', kwargs)
... 
>>> my_final_function('Python', 'The', 'Best', language="Python", users="A lot")
Type args:  <class 'tuple'>
args:  ('Python', 'The', 'Best')
Type kwargs:  <class 'dict'>
kwargs:  {'language': 'Python', 'users': 'A lot'}

A variável interna do kwargs sempre se transforma em um dicionário, que armazena os pares chave-valor passados ​​para a função.

Finalmente, vamos usar args e kwargs na mesma função:

Conclusão

  • Os operadores de descompactação são realmente úteis nas tarefas do dia-a-dia, agora você sabe como usá-los tanto em instruções individuais quanto em parâmetros de função.
  • Neste tutorial você aprendeu:
  • Você usa * para tuplas e listas e ** para dicionários
  • Você pode usar operadores de descompactação em construtores de funções e classes

args são usados ​​para passar parâmetros sem palavras-chave para funçõeskwargs são usados ​​para passar parâmetros com palavras-chave para funções.