Primeiramente vamos entender o que são iteradores. De acordo com o Wikipedia, um iterador é um objeto que possibilita ao desenvolvedor percorrer um conjunto de dados, particurlamente listas. O iterador dá acesso aos elementos dentro do conjunto de dados, mas ele não faz a iteração. Você deve estar confuso, então vamos mais devagar. Existem três partes chamadas:
- Iterável
- Iterador
- Iteração
Todas essas partes estão conectadas uma com as outras. Iremos discutir cada uma.
Um objeto iterável é qualquer objeto em Python que possui um método __iter__ ou um método
__getitem__ definido que retorna um iterador ou que pode ter índices (Ambos os métodos já
foram explicados em capítulos anteriores). Em resumo, um objeto iterável é qualquer objeto
que nos fornece um iterador. E, o que é um iterador?
Um iterador é qualquer objeto em Python que possui um método next (Python2)
ou um método __next__ definido. Agora vamos entender o que é iteração.
Em poucas palavras, iteração é o processo de pegar um item de algum coisa, por exemplo uma lista. Quando nós utilizamos um laço para iterar alguma coisa é chamado de iteração. É o nome dado ao próprio processo. Agora que nós já sabemos o básico desses termos vamos entender geradores.
Geradores são iteradores, mas você só consegue iterar com eles uma vez.
Isso acontece porque eles não armazenam todos os valores na memória, eles geram
os valores em tempo real. Você os utiliza iterando sobre eles, sendo com um laço 'for'
ou passando eles para alguma função ou construtor que itera. A maior parte do tempo
geradores são implemenatados como funções. No entanto, eles não retornam um valor, eles
produzem um. Abaixo você encontrará um exemplo simples de uma função geradora.
def generator_function():
for i in range(10):
yield i
for item in generator_function():
print(item)
# Output: 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9Nesse exemplo a função não é muito útil. Geradores são melhores utilizados
para calcular grandes conjuntos de resultados (particurlamente cálculos envolvendo
laços) onde você não deseja alocar a memória para todos os resultados ao mesmo tempo.
Muitas funções de Bibliotecas Padrão que retornam listas no Pytho 2 estão sendo modificadas
para retornar geradores no Python 3 porque geradores precisam de menos recursos.
Aqui segue um exemplo de gerador que calcula a sequência de Fibonacci:
# generator version
def fibon(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a + bE nós podemos utilizar a função da seguinte forma:
for x in fibon(1000000):
print(x)Dessa forma nós não precisamos nos preocupar se a função está utilizando muitos recursos. No entanto, se tivéssemos implementado assim:
def fibon(n):
a = b = 1
result = []
for i in range(n):
result.append(a)
a, b = b, a + b
return resultA função teria utilizado todos os nossos recursos para calcular uma entrada muito grande.
Discutimos anteriormente que podemos iterar sobre geradores apenas uma vez, mas não chegamos a testar isso.
Antes de testar, você precisa saber sobre mais uma função embutida do Python, next(). Ela nos permite
acessar o próximo elemento da sequência. Vamos testar:
def generator_function():
for i in range(3):
yield i
gen = generator_function()
print(next(gen))
# Output: 0
print(next(gen))
# Output: 1
print(next(gen))
# Output: 2
print(next(gen))
# Output: Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIterationComo você pode ver, depois de produzir todos os valores, next() provocou
um erro` StopIteration`. Basicamente esse erro nos informa que todos os valores já foram
produzidos. Você deve está se perguntando o motivo pelo qual esse erro não foi causado quando
usamos o laço for? Bom, a resposta é simples. O laço for automaticamente pega o erro e
encerra a chamada do método next. Você sabia que alguns tipos de dados embutidos de Python
também suportam iteração? Vamos dar uma olhada:
my_string = "Yasoob"
next(my_string)
# Output: Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: str object is not an iteratorBom, isso não é o que esperávamos. O erro diz que str não é um iterador.
E isso está certo! str é iterável, mas não é um iterador. Isso significa que str
suporta iteração, mas nós não podemos iterar sobre ela diretamente. Então, como podemos
iterar sobre ela? É hora de aprender sobre mais uma função embutida do Python, iter.
Ela retorna um objeto iterador a partir de um objeto iterável. Enquanto um int
não é iterável, nós podemos utilizá-lo em uma string que é iterável.
int_var = 1779
iter(int_var)
# Output: Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: 'int' object is not iterable
# This is because int is not iterable
my_string = "Yasoob"
my_iter = iter(my_string)
next(my_iter)
# Output: 'Y'Agora está muito melhor. Tenho certeza que você amou aprender sobre geradores.
Tenha em mente que você só entenderá completamento o conceito quando utilizá-lo.
Tenha certeza de seguir esse padrão e utilizar geradores sempre que fizer sentido
para você. Você não ficará desapontado!.