Entendendo o desempacotamento no Python
Quando estamos programando é bem comum termos listas de dados, listas de tarefas, de produtos, de clientes... No dia a dia nós fazemos muitas operações que envolvem listas.
Em uma parte de um programa que estou trabalhando, preciso pegar o nome e o sobrenome de alguns alunos cadastrados no banco de dados. Para isso, existe uma função que nos retorna o nome e o sobrenome de um aluno dado um id:
>>> aluno = busca_aluno(9)
>>> aluno
['Yuri', 'Oliveira']
Eu quero mostrar para o usuário uma mensagem de bem-vindo quando ele acessar o sistema:
>>> print('Bem-vindo {}'.format(aluno[0]))
Bem-vindo Yuri
Legal! Conseguimos imprimir nossa mensagem! Mas o que significa aluno[0]
? O login? O nome do usuário? O sobrenome?
Veja que o nosso código não tem muita semântica, isto é, quando alguém for ler nosso código, ou nós mesmos no futuro, não saberemos o que aluno[0]
significa só de olhar o código. Claro, nós podemos atribuir aluno[0]
a uma variável:
>>> nome = aluno[0]
>>> nome
'Yuri'
Mas, mesmo assim, a legibilidade do código fica um pouco prejudicada. O que tem dentro da lista aluno
? Um nome e sobrenome, uma lista de nome de vários alunos e estamos pegando só o primeiro’?
Veja que mesmo nesse caso, nós precisamos parar e interpretar o código, já que a leitura não está tão fluida. Hoje sabemos que a função busca_aluno
nos retorna o nome e o sobrenome, mas e amanhã? Talvez, quando nós, ou outra pessoa, no futuro olhar a variável aluno
não vai saber o que ela significa de fato.
Como nós podemos resolver isso já que nossa função busca_aluno
nos retorna uma lista com o nome e sobrenome do aluno?
Desembrulhando nossa lista
Nossa função busca_aluno
já nos retorna o nome e o sobrenome de um aluno. Então se fizermos algo assim será que funciona?
>>> nome, sobrenome = busca_aluno(9)
Em muitas linguagens de programação, esse código não funciona, mas em Python sim! Com esse código, estamos querendo dizer para o Python pegar o primeiro valor da lista e colocar na nossa variável nome
e o segundo valor na variável sobrenome
:
>>> nome
'Yuri'
>>> sobrenome
'Oliveira'
Nossa função retorna uma lista com dois elementos, nome e sobrenome, mas e se tivéssemos uma lista com mais valores? Por exemplo, se tivéssemos uma lista com dez números e queremos pegar os dois primeiros, precisaríamos declarar dez variáveis? Uma para cada elemento da lista?
Bem, nós só queremos os dois primeiros números então talvez nós possamos declarar só duas variáveis.
>>> numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x, y = numeros
E quando nós executarmos nosso código, teremos um erro:
Um erro de valor. Ele diz que temos muitos valores para desempacotar. Desempacotamento é o nome dessa técnica no Python. Neste caso, o erro nos diz que temos muitos valores para desempacotar, isto é, temos mais valores na nossa lista do que variáveis para atribuí-las.
Isso significa que precisamos mesmo declarar dez variáveis?
A expressão estrelada
Neste caso, nós queremos pegar os primeiros valores da lista e deixar o restante, ou seja, queremos uma variável para o primeiro elemento, uma para o segundo e uma para o restante:
>>> x, y, *restante = numeros
>>> x
0
>>> y
1
>>> restante
[2, 3, 4, 5, 6, 7, 8, 9]
Veja que antes da última variável colocamos um asterisco. Com isso estamos dizendo ao Python que nossa variável restante
receberá os elementos restantes da lista. Essa expressão é conhecida como starred expression.
Podemos passar essa expressão em qualquer lugar da nossa atribuição. Por exemplo, se queremos pegar os dois primeiros elementos da lista usamos uma expressão parecida com a anterior, mas e se quisermos pegar os dois últimos elementos?
Neste caso, basta colocar a starred expression no começo:
>>> *restante, x, y = numeros
>>> x
8
>>> y
9
>>> restante
[0, 1, 2, 3, 4, 5, 6, 7]
E também podemos colocar a starred expression no meio, dessa forma, nossa primeira variável terá o primeiro valor da lista, e a última variável terá o último valor da lista:
>>> x, *restante, y = numeros
>>> x
0
>>> y
9
>>> restante
[1, 2, 3, 4, 5, 6, 7, 8]
Além de listas, nós podemos desempacotar tuplas, dicionários e outros itens. No caso o dicionário retornará suas chaves.
Essa é uma forma de desempacotamento com o Python, será que existem outras?
Os parâmetros das funções
Além de desempacotar valores para atribuirmos em variáveis, nós podemos desempacotá-los para atribuir valores a parâmetros de funções.
Por exemplo, temos uma função que, passado dois números, nos retorna o maior valor:
def maior(num1, num2):
if num1 > num2:
return num1
return num2
E temos uma lista com dois valores que queremos passar como parâmetros da função. Nós podemos passar esses valores passando a posição deles na lista:
>>> numeros = [4, 9]
>>> maior_numero = maior(numeros[0], numeros[1])
>>> print(maior_numero)
9
Ou então, podemos desempacotar esses valores:
>>> numeros = [4, 9]
>>> maior_numero = maior(*numeros)
>>> print(maior_numero)
9
Nesse caso, o asterisco indica para o Python que o primeiro valor da nossa lista será passado como primeiro parâmetro e o segundo valor da nossa lista será passado com segundo parâmetro.
No caso de passar essa expressão em chamadas de funções, devemos nos atentar para que nossa lista não tenha elementos a menos, ou a mais, dos parâmetros da função, porque, se tiver, causará uma exceção:
>>> numeros = [4, 9, 27]
>>> maior_numero = maior(*numeros)
Nesse caso, o erro nos diz que a função maior
só recebe dois parâmetros posicionais e nós estamos tentando passar três.
Para saber mais
Nós também podemos desempacotar tuplas e dicionários nos parâmetros das funções. No caso dos dicionários, o desempacotamento gera parâmetros nomeados. Ou seja, as chaves do dicionário identificam o parâmetro e o valor do dicionário identifica o valor do parâmetro.
Por exemplo, temos uma função escreve_mensagem
que recebe uma mensagem e um nome:
def escreve_mensagem(mensagem, nome):
print(f'{mensagem}, {nome}')
Essa forma e formatar strings está disponível a partir da versão 3.6 do Python. Nela, podemos passar as variáveis, ou outras expressões diretamente na string, basta utilizar a letra
f
antes das aspas e colocar as variáveis entre chaves. Isso é chamado def-strings
Nós podemos ter um dicionário com as chaves mensagem e nome e passar para a nossa função desempacotando-o:
>>> mensagem = {'mensagem': 'Bem-vindo', 'nome': 'Yuri'}
>>> escreve_mensagem(**mensagem)
Bem-vindo, Yuri
Perceba que nesse caso, estamos passando dois asteriscos, pois estamos desempacotando um dicionário. Com essa expressão estamos dizendo ao Python que os parâmetros mensagem
e nome
da nossa função, vão receber os mesmos valores das chaves mensagem
e nome
do nosso dicionário.
Veja que no caso da nossa função busca_aluno
sempre nos retorna o sobrenome do aluno que precisa ser atribuído em uma variável para o desempacotamento funcionar.
Mas nós não estamos utilizando o sobrenome ainda e er uma variável no sistema que não utilizamos ocupa um espaço desnecessário na memória.
E se a gente falasse para o Python ignorar (_
) segundo valor da lista, isto é, ignorar o sobrenome?
>>> nome, _= busca_aluno(9)
>>> nome
'Yuri'
Com o operador _
estamos falando para o Python ignorar o segundo valor da lista. Com isso temos apenas a variável nome. Esse operador pode ser usado em conjunto com a starred expression:
>>> numeros = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x, y, *_ = numeros
>>> x
0
>>> y
1
E aí, o que achou dessa forma de desempacotar dados com o Python?
O Python se tornou uma linguagem muito utilizada pelos desenvolvedores. Por ter uma curva de aprendizado baixa e uma sintaxe amigável, ela ganhou muito espaço tanto com as pessoas que estão começando a programar quanto com os programadores mais experientes.
Aqui na Alura, temos uma vários cursos sobre Python, nela você aprenderá a sintaxe da linguagem, como criar funções e classes, conceitos como encapsulamento, até padrões de projeto, e frameworks web, como Flask e Django.