Python: Dois truques geniais que ninguém te conta

List comprehensions e yield

Christian Costa
Data Hackers
4 min readOct 21, 2019

--

Ao programar conforme vamos evoluindo queremos simplificar e tornar tudo o mais eficiente possível. Por mais simples que Python seja comparada à outras linguagens, existem algumas práticas que podem ajudar a simplificar ainda mais as coisas. Nesse post vamos dar uma olhada em list comprehensions e yield.

Com list comprehension é possível criar uma lista numérica qualquer com apenas uma linha de código e com o yield podemos gerar valores dentro de um loop a medida que precisamos. Vamos lá!

List Comprehension

A utilização de list comprehension tem o objetivo de simplificar o código utilizando loops ao criar listas numéricas.

Suponha que você precise criar uma lista com os triplos de todos os números entre um e dez, ou seja, para [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] você deve obter [3, 6, 9, 12, 15, 18, 27, 30], se você nunca usou list comprehensions (compreensão de listas) é provável que pense em algo mais ou menos assim:

Pra gerar essa lista de triplos utilizamos sete linhas. Até daria pra melhorar um pouco colocando a operação de multiplicação direto no append():

Mas a ideia aqui é ir além e economizar várias linhas, de forma relativamente simples. Uma list comprehension é feita da seguinte forma:

Isso pode ser bem confuso no começo, por isso vamos lá explicar ponto a ponto.

No exemplo “convencional” de como gerar uma lista de triplos, primeiro nos criamos a lista vazia, em seguida definimos o for com o range desejado, criamos a operação dentro do for e por fim Fazemos o append() na lista vazia

Para a compreensão criamos a lista e dentro dela já colocamos qual a operação desejada(numero * 3) e em seguida passamos o loop for com o range no qual queremos que a operação seja realizada

Portanto, diferente da resolução convencional, aqui criamos a sequência de números diretamente dentro da lista na qual desejamos que eles sejam armazenados e invertemos a ordem loop-operação a qual estamos habituados. A estrutura básica de uma lista comprehension é:

Usando If

Além de usar loops para gerar a lista de forma mais eficiente eu também posso fazer testes condicionais dentro das listas. Por exemplo, se eu quiser gerar uma lista que contenha apenas números pares. Usando list comprehension eu poderia fazer algo mais ou menos assim:

Observe que logo após especificar meu loop eu realizo o teste condicional, dessa forma somente os valores que passam pelo teste são colocados dentro dessa lista.

Comprehension, teste condicional e teste lógico

Também é possível testar condições que atendem a dois requisitos lógicos. No exemplo abaixo devemos gerar uma lista que contenha apenas os números que sejam simultaneamente múltiplos de três e cinco:

Exponenciação

Uma vez aprendida a lógica por trás do uso da compreensão, podemos realizar qualquer operação que faríamos usando um for convencional. No exemplo abaixo criamos uma lista com o cubo de todos os números entre 1 e 5:

Yield

Suponhamos que eu queira gerar uma lista de números de um até cinco. Num post anterior mencionei que isso pode ser feito com o loop for, dessa forma:

Agora, caso eu queira, digamos, uma função que faça essa tarefa posso declarar uma função, colocar o loop dentro dela e retornar o valor de n, veja abaixo:

Em ambos os exemplos devemos nos atentar para duas coisas: o loop for e o return.

O uso do for, é simplesmente gerar um range de números especificado, no nosso caso um à cinco. Dentro da função temos oreturn, que nos retorna os números conforme o loop segue, um por um.

Entretanto, por mais que seja retornado um número por vez, todos os números são gerados de uma vez. Isso significa que antes de exibir cada número, esses números já estão utilizando a memória do computador.

Por estamos lidando com um range de apenas cinco, isso não é um problema, mas basta aumentar o range para milhares, centenas de milhares ou ainda milhões e isso passa a afetar de maneira significativa o gerenciamento de memória do seu computador.

Um range de 1000000000 vai gerar 1000000000 números na sua memória

Para trabalhar com ranges muito grandes em Python temos algo chamado generator, mais especificamente nesse caso uma função que utiliza um generator. Usando esse tipo de função, ao invés de gerar N elementos e exibi-los um a um, eu posso gerar e exibir a medida que for necessário.

A estrutura básica de uma função que usa esse tipo de estrutura é muito semelhante às funções comuns, vejamos:

Observe que essa função é exatamente a mesma que utilizamos anteriormente para gerar um range entre um e cinco com exceção do yield. Esse cara é o responsável por “guardar” em qual número ele parou desde a última execução e retomar de onde parou quando for executado novamente.

Uma outra forma de trabalhar com um generator seria a seguinte:

Ao invés de utilizar a função sem nenhum parâmetro e especificar o range no loop utilizamos o parâmetro como referência e passamos ele ao chamar a função. O que poderia parecer algo assim:

Criamos a variável resultados e guardamos a funcao_generator dentro dela passando como parâmetro o range(5) , damos um print() e ela deveria exibir os valores. Deveria, pois executar esse código irá gerar um erro.

Para ver os números nesse range eu preciso fazer o seguinte:

Aqui usamos um for , e passamos a quantidade de valores que desejamos exibir (nesse caso, range(5+1)).

Return ou Yield?

Ambos tem funções similares mas isso não significa que um substitui o outro. O uso de cada um deve ser escolhido de forma adequada com a necessidade. Para obter sequências de valores sem antes gerar todos esses e armazenar em memória a melhor escolha seria o yield, para casos onde não há essa necessidade o return pode fazer muito bem o trabalho!

--

--

Christian Costa
Data Hackers

“Pelos livros que eu tive acesso minha obrigação é espalhar pro resto”