Diferença entre Geradores e Iteradores do Python
Qual é a diferença entre iteradores e geradores? Alguns exemplos de quando você usaria cada caso seriam úteis.
— newToProgramming
iteratoré um conceito mais geral: qualquer objeto cuja classe tenha um nextmétodo ( __next__no Python 3) e um __iter__método que o faça return self.
Todo gerador é um iterador, mas não vice-versa. Um gerador é criado chamando uma função que possui uma ou mais yieldexpressões ( yieldinstruções, no Python 2.5 e anteriores) e é um objeto que atende à definição de an do parágrafo anterior iterator.
Você pode usar um iterador personalizado, em vez de um gerador, quando precisar de uma classe com um comportamento de manutenção de estado um tanto complexo ou desejar expor outros métodos além de next(e __iter__e __init__). Na maioria das vezes, um gerador (às vezes, para necessidades suficientemente simples, uma expressão de gerador ) é suficiente e é mais simples de codificar porque a manutenção do estado (dentro de limites razoáveis) é basicamente "feita por você", pois o quadro é suspenso e retomado.
Por exemplo, um gerador como:
ou a expressão equivalente do gerador (genexp)
levaria mais código para criar como um iterador personalizado:
def next(self): # __next__ in Python 3
Mas, é claro, com a aula Squaresvocê poderia facilmente oferecer métodos extras, ou seja,
se você tiver alguma necessidade real dessa funcionalidade extra em seu aplicativo.
como uso o iterador, depois de criá-lo?
— Vincenzooo
@Vincenzooo que depende do que você quer fazer com ele. Será parte de um for ... in ...:, passado para uma função, ou você estará ligandoiter.next()
Caleth @ Eu estava perguntando sobre a sintaxe exata, porque estava recebendo erro ao tentar usar a for..insintaxe. Talvez eu estivesse perdendo alguma coisa, mas isso foi há algum tempo atrás, não me lembro se resolvi. Obrigado!
Em resumo: Iteradores são objetos que possuem um método __iter__e __next__( nextno Python 2). Os geradores fornecem uma maneira fácil e integrada de criar instâncias de iteradores.
Uma função com rendimento ainda é uma função que, quando chamada, retorna uma instância de um objeto gerador:
Uma expressão de gerador também retorna um gerador:
Para uma exposição e exemplos mais detalhados, continue lendo.
Um gerador é um iterador
Especificamente, o gerador é um subtipo de iterador.
Podemos criar um gerador de várias maneiras. Uma maneira muito comum e simples de fazer isso é com uma função.
Especificamente, uma função com rendimento nela é uma função que, quando chamada, retorna um gerador:
E um gerador, novamente, é um Iterador:
Um iterador é um iterável
Um iterador é um iterável,
que requer um __iter__método que retorna um iterador:
Alguns exemplos de iteráveis ​​são as tuplas, listas, dicionários, conjuntos, conjuntos congelados, cadeias, cadeias de bytes, matrizes de bytes, intervalos e visões de memória:
Iteradores requerem um nextou __next__método
E no Python 3:
Podemos obter os iteradores dos objetos internos (ou objetos personalizados) com a iterfunção:
O __iter__método é chamado quando você tenta usar um objeto com um loop for. Em seguida, o __next__método é chamado no objeto iterador para obter cada item do loop. O iterador aumenta StopIterationquando você o esgotou e não pode ser reutilizado nesse ponto.
A partir da documentação
Na seção Tipos de Gerador da seção Tipos de Iterador da documentação de Tipos Internos :
Os geradores do Python fornecem uma maneira conveniente de implementar o protocolo do iterador. Se um objecto do recipiente __iter__()método é implementado como um gerador, ele voltará automaticamente um objecto iteração (tecnicamente, um objecto gerador) fornecendo o __iter__()e next()[ __next__()em Python 3] métodos. Mais informações sobre geradores podem ser encontradas na documentação para a expressão de rendimento.
Portanto, aprendemos que os geradores são um tipo (conveniente) de iterador.
Objetos Iteradores de Exemplo
Você pode criar um objeto que implemente o protocolo Iterator criando ou estendendo seu próprio objeto.
Mas é mais fácil simplesmente usar um gerador para fazer isso:
Ou talvez mais simples, uma Expressão de Gerador (funciona de maneira semelhante à lista de compreensões):
Todos eles podem ser usados ​​da mesma maneira:
Você pode usar o protocolo Iterator diretamente quando precisar estender um objeto Python como um objeto que pode ser iterado.
No entanto, na grande maioria dos casos, você é o mais indicado yieldpara definir uma função que retorna um iterador de gerador ou considera expressões de gerador.
Por fim, observe que os geradores fornecem ainda mais funcionalidade como corotinas. Explico Generators, juntamente com a yielddeclaração, em profundidade na minha resposta para "O que a palavra-chave" yield "faz?".
Iterador são objetos que usam o next()método para obter o próximo valor da sequência.
Um gerador é uma função que produz ou produz uma sequência de valores usando o yieldmétodo
Toda next()chamada de método no objeto gerador (por exemplo: fcomo no exemplo abaixo) retornada pela função do gerador (por exemplo: foo()função no exemplo abaixo) gera o próximo valor na sequência.
Quando uma função geradora é chamada, ela retorna um objeto gerador sem iniciar a execução da função. Quando o next()método é chamado pela primeira vez, a função começa a ser executada até atingir a instrução yield que retorna o valor gerado. O rendimento controla ie lembra da última execução. E a segunda next()chamada continua do valor anterior.
O exemplo a seguir demonstra a interação entre rendimento e chamada para o próximo método no objeto gerador.
Apenas FYI rendimento não é método, da palavra-chave
Adicionando uma resposta porque nenhuma das respostas existentes aborda especificamente a confusão na literatura oficial.
Funções de gerador são funções comuns definidas usando emyieldvez dereturn. Quando chamada, uma função de gerador retorna um objeto gerador , que é um tipo de iterador - ele possui umnext()método. Quando você chamanext(), o próximo valor gerado pela função gerador é retornado.
A função ou o objeto pode ser chamado de "gerador", dependendo do documento de origem Python que você lê. O glossário do Python diz que funções de gerador, enquanto o wiki do Python implica objetos de gerador. O tutorial do Python notavelmente implica em ambos os usos no espaço de três frases:
Os geradores são uma ferramenta simples e poderosa para criar iteradores. Eles são escritos como funções regulares, mas usam a declaração de rendimento sempre que desejam retornar dados. Cada vez que next () é chamado, o gerador continua de onde parou (lembra todos os valores de dados e qual instrução foi executada pela última vez).
As duas primeiras frases identificam geradores com funções geradoras, enquanto a terceira frase os identifica com objetos geradores.
Apesar de toda essa confusão, pode-se procurar a referência da linguagem Python para a palavra clara e final:
A expressão de rendimento é usada apenas ao definir uma função geradora e só pode ser usada no corpo de uma definição de função. O uso de uma expressão de rendimento em uma definição de função é suficiente para fazer com que essa definição crie uma função geradora em vez de uma função normal.
Quando uma função de gerador é chamada, ela retorna um iterador conhecido como gerador. Esse gerador controla a execução de uma função do gerador.
Assim, no uso formal e preciso, "gerador" não qualificado significa objeto gerador, não função gerador.
As referências acima são para Python 2, mas a referência à linguagem Python 3 diz a mesma coisa. No entanto, o glossário Python 3 afirma que
gerador ... Geralmente se refere a uma função de gerador, mas pode se referir a um iterador de gerador em alguns contextos. Nos casos em que o significado pretendido não é claro, o uso de termos completos evita ambiguidade.
Eu não acho que exista muita confusão entre funções de gerador e objetos de gerador, pelo mesmo motivo geralmente não há confusão entre classes e suas instâncias. Nos dois casos, você chama um para obter o outro e, em conversas informais (ou documentação rapidamente escrita), pode usar o nome da classe ou a palavra "gerador" para qualquer um. Você só precisa ser explícito sobre "função de gerador" versus "objeto de gerador" em raras situações em que você está falando.
1. Independentemente das razões teóricas pelas quais não deve haver confusão, os comentários sobre outras respostas a essa pergunta negam e se contradizem sem resolução, indicando que existe uma confusão real. 2. Imprecisão casual é boa, mas uma fonte precisa e autorizada deve ser pelo menos uma das opções no SO. Uso extensivamente funções e objetos de gerador no meu projeto atual, e a distinção é muito importante ao projetar e codificar. É bom saber que terminologia usar agora, então não preciso alterar dezenas de nomes e comentários de variáveis ​​posteriormente.
Imagine uma literatura matemática em que não seja feita distinção entre uma função e seu valor de retorno. Ocasionalmente, é conveniente confundi-los informalmente, mas aumenta o risco de vários erros. A matemática moderna avançada seria significativamente e desnecessariamente prejudicada se a distinção não fosse formalizada em convenção, idioma e notação.
Funções de ordem superior passando por geradores ou funções de gerador podem parecer estranhas, mas para mim elas estão surgindo. Estou trabalhando no Apache Spark e ele impõe um estilo de programação muito funcional. As funções precisam criar, passar e distribuir todos os tipos de objetos para fazer as coisas. Eu tive várias situações em que perdi a noção de que tipo de "gerador" eu estava trabalhando. Dicas sobre nomes de variáveis ​​e comentários, usando a terminologia consistente e correta, ajudaram a esclarecer a confusão. A obscuridade de um Pythonist pode ser o centro do design do projeto de outro!
@Paul, obrigado por escrever esta resposta. Essa confusão é importante porque a diferença entre um objeto gerador e uma função gerador é a diferença entre obter o comportamento desejado e ter que procurar geradores.
Todo mundo tem uma resposta muito boa e detalhada com exemplos e eu realmente aprecio isso. Eu só queria dar algumas respostas curtas para pessoas que ainda não são muito claras conceitualmente:
Se você criar seu próprio iterador, ele será um pouco envolvido - você precisará criar uma classe e pelo menos implementar o iter e os próximos métodos. Mas e se você não quiser passar por esse aborrecimento e quiser criar rapidamente um iterador. Felizmente, o Python fornece uma maneira rápida de definir um iterador. Tudo o que você precisa fazer é definir uma função com pelo menos 1 chamada para produzir e agora, quando você chamar essa função, ela retornará " algo " que funcionará como um iterador (você pode chamar o próximo método e usá-lo em um loop for). Este algo tem um nome em Python chamado Generator
Espero que isso esclareça um pouco.
— Heapify
As respostas anteriores perderam essa adição: um gerador tem um closemétodo, enquanto os iteradores típicos não. O closemétodo aciona uma StopIterationexceção no gerador, que pode ser capturada em uma finallycláusula nesse iterador, para ter a chance de executar alguma limpeza. Essa abstração o torna mais utilizável nos iteradores grandes do que simples. Pode-se fechar um gerador como se pode fechar um arquivo, sem ter que se preocupar com o que está por baixo.
Dito isso, minha resposta pessoal à primeira pergunta seria: iterável possui __iter__apenas um método, iteradores típicos têm __next__apenas um método, geradores possuem um __iter__e um __next__e um adicional close.
Para a segunda pergunta, minha resposta pessoal seria: em uma interface pública, tendem a favorecer muito os geradores, uma vez que é mais resiliente: o closemétodo com uma maior composibilidade yield from. Localmente, posso usar iteradores, mas apenas se for uma estrutura plana e simples (os iteradores não se compõem facilmente) e se houver motivos para acreditar que a sequência seja bastante curta, especialmente se for interrompida antes de chegar ao fim. Costumo olhar os iteradores como um primitivo de baixo nível, exceto como literais.
Para questões de fluxo de controle, os geradores são um conceito tão importante quanto as promessas: ambos são abstratos e composíveis.
Você poderia dar um exemplo para ilustrar o que você quer dizer quando fala sobre composição? Além disso, você pode explicar o que tem em mente ao falar sobre " iteradores típicos "?
Outra resposta ( stackoverflow.com/a/28353158/1878788 ) afirma que "um iterador é iterável". Como um iterável tem um __iter__método, como um iterador pode ter __next__apenas? Se eles deveriam ser iterables, eu esperaria que eles __iter__também o tivessem .
@bli: AFAICS, esta resposta aqui se refere ao padrão PEP234 , portanto está correta, enquanto a outra resposta se refere a alguma implementação, por isso é questionável. O padrão requer apenas que um __iter__iterables retorne um iterador, que requer apenas um nextmétodo ( __next__no Python3). Por favor, não confunda padrões (para digitação de pato) com sua implementação (como um interpretador Python em particular o implementou). É um pouco como a confusão entre as funções do gerador (definição) e os objetos do gerador (implementação). ;)
Função gerador, objeto gerador, gerador:
Uma função Generator é como uma função regular no Python, mas contém uma ou mais yieldinstruções. As funções do gerador são uma ótima ferramenta para criar objetos Iterator da maneira mais fácil possível. A função de retorno de objeto Iterator também é chamada de objeto Generator ou Generator .
Neste exemplo, eu criei uma função Generator que retorna um objeto Generator <generator object fib at 0x01342480>. Assim como outros iteradores, os objetos Generator podem ser usados ​​em forloop ou com a função next()interna que retorna o próximo valor do gerador.
Portanto, uma função geradora é a maneira mais fácil de criar um objeto Iterator.
Iterador :
Todo objeto gerador é um iterador, mas não vice-versa. Um objeto iterador personalizado pode ser criado se sua classe implementar __iter__e __next__método (também chamado de protocolo iterador).
No entanto, é muito mais fácil usar a função geradores para criar iteradores porque eles simplificam sua criação, mas um Iterador personalizado oferece mais liberdade e você também pode implementar outros métodos de acordo com seus requisitos, conforme mostrado no exemplo abaixo.
— N Randhawa
Exemplos de Ned Batchelder altamente recomendados para iteradores e geradores
Um método sem geradores que fazem algo com números pares
enquanto usando um gerador
Não precisamos de nenhuma lista nem returndeclaração
Eficiente para fluxo de comprimento grande / infinito ... apenas caminha e gera o valor
Chamar o evensmétodo (gerador) é como sempre
Gerador também usado para quebrar loop duplo
Um livro cheio de páginas é iterável , Um marcador é iterador
e este marcador não tem nada a fazer, exceto mover next
Para usar o Generator ... precisamos de uma função
Para usar o Iterator ... precisamos nexteiter
Uma função Generator retorna um objeto iterador
Todo o benefício do Iterator:
Armazene um elemento por vez na memória
— Marwan Mostafa
Sobre o seu primeiro trecho de código, eu gostaria de saber o que mais poderia ser o 'stream' do que a lista []?
— Iqra.
Você pode comparar as duas abordagens para os mesmos dados:
Além disso, se você verificar a área ocupada pela memória, o gerador ocupará muito menos memória, pois não precisará armazenar todos os valores na memória ao mesmo tempo.
Estou escrevendo especificamente para iniciantes em Python de uma maneira muito simples, embora no fundo o Python faça muitas coisas.
Vamos começar com o muito básico:
Considere uma lista,
Vamos escrever uma função equivalente:
o / p de print(l): [1,2,3]& o / p deprint(f()) : [1,2,3]
Vamos tornar a lista iterável: na lista python é sempre iterável, o que significa que você pode aplicar o iterador sempre que quiser.
Vamos aplicar o iterador na lista:
Vamos tornar uma função iterável, ou seja, escrever uma função geradora equivalente. Em python, assim que você introduzir a palavra-chave yield; torna-se uma função de gerador e o iterador será aplicado implicitamente.
Nota: Todo gerador é sempre iterável com o iterador implícito aplicado e aqui o iterador implícito é o ponto crucial. Portanto, a função do gerador será:
Portanto, se você observou, assim que criou a função de gerador, ele já é iter (f)
l é a lista, depois de aplicar o método iterador "iter", iter (l)
f já é iter (f), após aplicar o método iterador "iter", ele se torna iter (iter (f)), que é novamente iter (f)