Quando você deve usar expressões geradoras e quando deve usar a compreensão de lista no Python?
# Generator expression
(x*2 for x in range(256))
# List comprehension
[x*2 for x in range(256)]
python
list-comprehension
generator
Somente leitura
fonte
fonte
[exp for x in iter]
ser apenas açúcarlist((exp for x in iter))
? ou existe uma diferença de execução?X = [x**2 for x in range(5)]; print x
comY = list(y**2 for y in range(5)); print y
, o segundo dará um erro. No Python3, uma compreensão de lista é realmente o açúcar sintático para uma expressão de gerador alimentadalist()
conforme o esperado, portanto a variável loop não vazará mais .Respostas:
A resposta de John é boa (essa compreensão da lista é melhor quando você deseja repetir várias vezes). No entanto, também vale a pena notar que você deve usar uma lista se quiser usar qualquer um dos métodos da lista. Por exemplo, o seguinte código não funcionará:
Basicamente, use uma expressão geradora se tudo o que você estiver fazendo for iterando uma vez. Se você deseja armazenar e usar os resultados gerados, provavelmente está melhor com uma compreensão da lista.
Como o desempenho é o motivo mais comum para escolher um sobre o outro, meu conselho é não se preocupar com isso e apenas escolher um; se você achar que seu programa está sendo executado muito devagar, só então você deve voltar e se preocupar em ajustar seu código.
fonte
a = [1, 2, 3] b = [4, 5, 6] a.extend(b)
- a agora será [1, 2, 3, 4, 5, 6]. (Você pode adicionar novas linhas em comentários ??)a = (x for x in range(0,10)), b = [1,2,3]
por exemplo.a.extend(b)
lança uma exceção.b.extend(a)
avaliará tudo, nesse caso, não há sentido em torná-lo um gerador em primeiro lugar.A iteração sobre a expressão do gerador ou a compreensão da lista fará a mesma coisa. No entanto, a compreensão da lista criará a lista inteira na memória primeiro, enquanto a expressão do gerador criará os itens rapidamente, para que você possa usá-lo para sequências muito grandes (e também infinitas!).
fonte
itertools.count(n)
é uma sequência infinita de números inteiros, iniciando em n, portanto(2 ** item for item in itertools.count(n))
seria uma sequência infinita dos poderes de2
começar em2 ** n
.Use a compreensão da lista quando o resultado precisar ser repetido várias vezes ou quando a velocidade for primordial. Use expressões geradoras onde o intervalo é grande ou infinito.
Consulte Expressões do gerador e compreensões da lista para obter mais informações.
fonte
lists
são mais rápidos quegenerator
expressões? Ao ler a resposta da dF, descobriu-se que era o contrário.O ponto importante é que a compreensão da lista cria uma nova lista. O gerador cria um objeto iterável que "filtra" o material de origem rapidamente enquanto você consome os bits.
Imagine que você tem um arquivo de log de 2 TB chamado "hugefile.txt" e deseja o conteúdo e o comprimento de todas as linhas que começam com a palavra "ENTRY".
Então, tente começar escrevendo uma lista de compreensão:
Isso reduz o arquivo inteiro, processa cada linha e armazena as linhas correspondentes em sua matriz. Essa matriz pode, portanto, conter até 2 TB de conteúdo. É muita RAM e provavelmente não é prático para seus propósitos.
Então, em vez disso, podemos usar um gerador para aplicar um "filtro" ao nosso conteúdo. Nenhum dado é realmente lido até começarmos a iterar sobre o resultado.
Ainda nem uma única linha foi lida em nosso arquivo. De fato, digamos que queremos filtrar ainda mais nosso resultado:
Ainda nada foi lido, mas especificamos agora dois geradores que atuarão em nossos dados conforme desejamos.
Vamos escrever nossas linhas filtradas para outro arquivo:
Agora lemos o arquivo de entrada. Como nosso
for
loop continua solicitando linhas adicionais, olong_entries
gerador exige linhas doentry_lines
gerador, retornando apenas aquelas cujo comprimento é superior a 80 caracteres. Por sua vez, oentry_lines
gerador solicita linhas (filtradas conforme indicado) aologfile
iterador, que por sua vez lê o arquivo.Portanto, em vez de "enviar" dados para sua função de saída na forma de uma lista totalmente preenchida, você está dando à função de saída uma maneira de "extrair" dados apenas quando necessário. No nosso caso, isso é muito mais eficiente, mas não tão flexível. Geradores são uma maneira, uma passagem; os dados do arquivo de log que lemos são imediatamente descartados; portanto, não podemos voltar à linha anterior. Por outro lado, não precisamos nos preocupar em manter os dados por perto quando terminarmos.
fonte
O benefício de uma expressão geradora é que ela usa menos memória, pois não cria a lista inteira de uma só vez. As expressões de gerador são mais usadas quando a lista é um intermediário, como a soma dos resultados ou a criação de um ditado a partir dos resultados.
Por exemplo:
A vantagem é que a lista não é completamente gerada e, portanto, pouca memória é usada (e também deve ser mais rápida)
No entanto, você deve usar a compreensão da lista quando o produto final desejado for uma lista. Você não vai salvar nenhuma memória usando expressões geradoras, pois deseja a lista gerada. Você também tem o benefício de poder usar qualquer uma das funções da lista, como classificada ou revertida.
Por exemplo:
fonte
sum(x*2 for x in xrange(256))
sorted
ereversed
funcione bem em quaisquer expressões geradoras iteráveis incluídas.Ao criar um gerador a partir de um objeto mutável (como uma lista), lembre-se de que o gerador será avaliado no estado da lista no momento da utilização do gerador, não no momento da criação do gerador:
Se houver alguma chance de sua lista ser modificada (ou um objeto mutável dentro dessa lista), mas você precisar do estado na criação do gerador, precisará usar uma compreensão da lista.
fonte
Às vezes, você pode se livrar da função tee dos itertools , que retorna vários iteradores para o mesmo gerador que pode ser usado independentemente.
fonte
Estou usando o módulo Hadoop Mincemeat . Eu acho que este é um ótimo exemplo para anotar:
Aqui, o gerador obtém números de um arquivo de texto (de até 15 GB) e aplica matemática simples nesses números usando o redutor de mapa do Hadoop. Se eu não tivesse usado a função yield, mas uma compreensão da lista, levaria muito mais tempo calculando as somas e a média (para não mencionar a complexidade do espaço).
O Hadoop é um ótimo exemplo para usar todas as vantagens dos geradores.
fonte