Meus 'truques' preferidos em Python - Parte I
Recentemente, estive ajudando um amigo que transicionava do Matlab para o Python. Dando algumas dicas para ele, percebi que muitas das nuances legais que eu aprendi no Python foi alguém que me ensinou em um momento de “você conhece isso?” ou para resolver um problema bem específico que poderia ser resolvido de forma mais simples.
Ao ajudar esse meu amigo que está lá do outro lado do mundo, lembrei da época em que não havia ninguém pra me ensinar um truque legal e, na verdade, não sabia nem que isso poderia existir.
Decidi então escrever meu truques favoritos e que, muitas vezes, deixam o código muito mais simples e legível. Esse texto vai estar entre o cheat list e um post simples e espero que seja útil.
Permuta de valores entre variáveis
Agora, imagine que temos duas variáveis e queremos inverter os valores delas. Eu quero que a variável a passe a ter o valor de b e vice-versa. Podemos usar a seguinte lógica:
a, b = 10, 5
a, b = b, a
Recuperando informações de dicionário
Dicionários são estruturas maravilhosas e que usamos frequentemente. Agora, vamos imaginar que você vai receber um dicionário
de um determinado lugar e você não tem certeza se uma determinada chave vai estar no dicionário. Como você vai fazer isso?
Se tentarmos buscar uma chave que não existe, teremos um retorno de KeyError
. Uma primeira maneira intuita, seria fazer um
try
. Por exemplo:
dicionario = {'Maria': '1235'}
try:
valor = dicionario['chave']
except KeyError:
valor = None
Parece muita complicação para uma coisa simples, né? Pois bem, os dicionários tem um método get
que busca uma chave e retorna
None
caso essa chave não exista:
valor = dicionario.get('chave')
E melhor ainda! Caso haja um valor padrão, você pode adicioná-lo como um segundo parâmetro.
valor = dicionario.get('chave', 123)
# caso a chave não exista:
>>> valor
123
Os condicionais estão muito compridos?
Uma situação que eu passei também foi ter que passar por if
s cujas verificações eram muito longas e o código ficava esquisito.
Imagine algo tipo isso:
if objeto.modelo.atributo['chave'] == 'alguma coisa aqui' and objeto.modelo.outro_atributo['outra_chave'] == 23 and ... :
pass
Você entendeu… muitos caracteres para pouca verificação. Vamos pegar um cenário onde temos 4 variáveis que devem ser
verificadas em um if
. Nosso código ficaria algo assim:
a, b, c, d = 1, 2, 3, 4
if a == 1 and b == 2 and c == 3 and d == 4:
print('todas as condições eram verdadeiras!')
Nesse caso, queremos que todas as condições sejam verdadeiras, certo? Cada verificador ==
vai retornar True
ou False
e,
portanto, entraremos no if
só se todos forem verdadeiros.
Vamos fazer um pouco diferente: vamos colocar todos os condicionais em uma lista:
condicoes = [
a==1,
b==2,
c==3,
d==4
]
Maravilha! Agora vamos usar a maravilhosa função all
para verificar se todas as condições são verdadeiras:
if all(condicoes):
print('todas as condições eram verdadeiras!')
Pronto! Muito mais simples e limpo e muito mais fácil de entender o que está acontecendo. Legal, né?
Agora, se quisermos que nem todas as condições sejam verdadeiras (ou seja, apenas 1 ser verdadeira já basta), podemos
usar a função any
:
if any(condicoes):
print('alguma condição era verdadeira!')
Para que precisamos de ‘else’?
Esse é um dos meus preferidos! Vamos pegar uma função modifica_valor
que recebe uma entrada e verifica se ela for igual a
a, ela retorna 0, de outra forma 1.
def modifica_valor(x):
if x == 'a':
return 0
else:
return 1
Nesse caso, o else
se torna completamente desnecessário. Porque qualquer caso cair no primeiro if
, já vai sair da função.
Dessa forma, nossa função pode ser simplificada para:
def modifica_valor(x):
if x == 'a':
return 0
return 1
Loops de uma linha só
Agora, quero falar sobre list comprehensions que eu estou carinhosamente chamando de loops de uma linha só. Esse é um conceito um pouco complexo, então eu não vou trazer uma longa exposição sobre todas as possibilidades que existem. Quero que você apenas visualize que isso é possível e, muitas vezes, útil. Ok?
Vamos para um exemplo inicial. Temos uma lista de inteiros e gostaríamos de criar uma nova lista que contenha os valores ao
quadrado. O que podemos fazer é criar uma lista nova (lista_modificada
), iterar sobre cada item da primeira lista e adicionar
à nova lista o valor ao quadrado. O resultado é exatamente o que gostaríamos.
lista = [1, 2, 3]
lista_modificada = []
for x in lista:
lista_modificada.append(x**2)
# lista_modificada
[1, 4, 9]
Perceba que “gastamos” 3 linhas em um problema relativamente simples. Com list comprehension, nós colocamos o for
dentro
de uma nova lista:
lista = [1, 2, 3]
lista_modificada = [x**2 for x in lista]
# lista_modificada
[1, 4, 9]
O que fizemos basicamente foi dizer: eleve ao quadrado cada item existente na minha lista
. Ao fazemos isso dentro de uma nova
lista, automaticamente dizemos que o novo item ao quadrado deve ser inserido na lista. Simples e “econômico”. Claro que existem
múltiplas formas de fazer isso e potencializar o uso desse método, mas você entendeu a ideia geral, espero!
Funções de uma linha só
Do mesmo jeito que podemos fazer loops em uma linha só também podemos criar funções de uma linha só. No Python, vamos
usar o lambda
para isso. No entanto, não se engane! Esse conceito, pra mim, foi muito difícil de entender e até hoje
eu tenho dificuldades em usar o lambda quando as coisas começam a ficar complicadas. De novo, isso aqui é apenas uma “ideia
geral” para que você se aprofunde.
Vou tentar explicar o porque usar o lambda em um exemplo prático.
Imaginemos o seguinte cenário: temos uma lista de itens e queremos transformar estes itens em valores de 0 ou 1.
lista = ['a', 'b', 'b', 'c', 'a', 'b']
lista_modificada = [0, 1, 1, 1, 0, 1]
Podemos fazer uma função que recebe a lista, varre cada um dos itens e retorn 0 ou 1 dependendo do item. Seria algo parecido com isso:
def modifica_valor(lista):
lista_modificada = []
for x in lista:
if x == 'a':
lista_modificada.append(0)
else:
lista_modificada.append(1)
Vamos tirar o loop dentro da lista da função. A função ficará responsável apenas por retornar o valor 0 ou 1 dependendo do valor do item.
def modifica_valor(x):
if x == 'a':
return 0
return 1
lista_modificada = []
for x in lista:
lista_modificada.append(modifica_valor(x))
Legal, agora a nossa função ficou bastante simples. Semelhante ao que fizemos no list comprehension podemos representar a mesma função da seguinte forma:
transform = lambda x: 0 if x == 'a' else 1
# type(transform)
>>> function
Ou seja, ao receber um x retorne 0 caso ele seja igual a a ou 1 em qualquer outro caso. Isso pode ser usado, por
exemplo, ao se passar uma função para um map
que vai aplicar essa função em cada item da lista. Aquele loop
com vários append
acaba virando isso:
lista_transform = list(map(transform, lista))
# lista_transform
>>> [0, 1, 1, 1, 0, 1]
Loops também tem else!
Uma das coisas que mais me surpreendeu quando eu aprendi, foi o fato de que loops também tem else
. São 3 casos que vou usar
para exemplificar o uso:
1. Passamos uma lista vazia
Quando passamos uma lista vazia para que o loop ocorra, nada acontecerá, mas o else
roda logo em seguida:
lista = []
for x in lista:
print('dentro do loop')
else:
print('dentro do else')
# resultado
'dentro do else'
2. Quando o loop acontece normalmente
Quando o loop acontece de forma normal, todo ele é executado normalmente e, ao fim, a condição dentro do else
é executada.
lista = [1, 2]
for x in lista:
print('dentro do loop')
else:
print('dentro do else')
# resultado
'dentro do loop'
'dentro do loop'
'dentro do else'
3. Deixando de executar o ‘else’
Se aplicarmos um break dentro do loop, o ciclo todo será interrompido e o código dentro do else
não será executado.
lista = [1]
for x in lista:
print('dentro do loop')
break
else:
print('dentro do else')
# resultado
'dentro do loop'
String comprehension?
Strings funcionam bem semelhantes a listas. Isso porque, na verdade, strings são listas de caracteres. Portanto, se fizermos uma iteração por uma string, o retorno será cada um dos caracteres.
palavra = 'ola'
for i in palavra:
print(i)
# resultado
o
l
a
Dessa forma, podemos fazer algo semelhante ao que vimos no list comprehension para fazer manipulações de strings. Por exemplo, vamos supor que você tenha uma string que contenha pontuações, e você deseja removê-las. Usando essa ideia simples, podemos fazer:
frase = 'O que? Eu não quero frases com pontuações! Chega!'
frase_sem_pontuacao = ''.join(char for char in frase if char not in [ '!', '?'])
A diferença entre o list comprehension e esse “string comprehension” é que o primeiro precisávamos colocar o loop entre
as chaves da lista, enquanto agora usamos o método join
da string vazia (''
) para adicionar o resultado. Bem legal né?
Checando um tipo
Se quisermos conferir se uma determinada variável é de um determinado tipo, usamos a função type
:
>>> type('oi')
str
Então seria natural pensar que ao conferirmos uma entrada usássemos algo parecido com:
type('oi') == str
Mas temos um método mais apropriado para fazer isso, que é o isinstance
, que confere se aquela variável é uma instância de
uma classe determinada:
isinstance('oi', str):
Bem simples e elegante :)
Adios!
É isso por hoje pessoal! Espero que tenham gostado e espero fazer novos posts conforme eu aprenda mais “truques”!
❤ Abraço! Letícia