5.1 Iterable
e Iterator
Como você já sabe, iteradores são objetos que implementam o protocolo iterador, permitindo obter elementos de uma coleção um de cada vez. Iteradores são amplamente usados em Python para percorrer elementos de sequências como listas, tuplas e strings.
Vamos ver como os iteradores são feitos e como usá-los.
Objeto iterável (Iterable)
Para que um objeto possa ser percorrido com um loop for
, ele deve
ser iterável – Iterable
. Isso significa que nosso objeto deve
implementar o método __iter__()
,
que retorna um objeto iterador.
Objeto iterador (Iterator)
Este é um objeto especial que tem
função __next__()
para retornar
o próximo elemento da sequência. Quando os elementos
acabam, o método __next__()
lança uma exceção
StopIteration
como sinal de encerramento da iteração.
O iterador também deve implementar o método __iter__()
,
que retorna o próprio iterador.
Exemplo usando funções embutidas do Python
Neste exemplo, a lista
numbers é um objeto iterável.
Nós obtemos um iterador usando a função
iter()
e usamos a função
next()
para percorrer os elementos até que
a exceção
StopIteration
seja lançada.
# Objeto iterável
numbers = [1, 2, 3, 4, 5]
# Obtemos um iterador do objeto iterável
iterator = iter(numbers)
# Usamos o iterador para percorrer os elementos
try:
while True:
number = next(iterator)
print(number)
except StopIteration:
pass
É isso que acontece quando você escreve um código assim:
# Objeto iterável
numbers = [1, 2, 3, 4, 5]
for number in numbers:
print(number)
5.2 Essência do iterador
Iterador é um objeto que nos ajuda a percorrer um grupo de elementos um de cada vez. As implementações podem variar bastante. Vamos escrever nossa própria classe, onde vamos implementar todos os requisitos para um iterador.
Passo 1. Primeiro, vamos criar nossa classe
Que tal fazer ela retornar números de start
a
end
sequencialmente?
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
Passo 2. Suporte para a função __iter__
Agora precisamos adicionar a função __iter__
,
que vai retornar um objeto iterador, no qual a função
__next()__
será chamada. Vamos retornar
uma referência ao próprio objeto – isso é permitido.
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
Passo 3. Suporte para a função __next__
Agora precisamos adicionar ao nosso objeto iterador a função __next__
, que vai retornar
o próximo elemento da nossa lista. Vamos simplesmente usar a variável current
:
def __next__(self):
current = self.current
self.current += 1
return current
Passo 4. Parada do iterador
Se o iterador já retornou todos os valores que pretendia, ele deve lançar uma exceção StopIteration
. Vamos
ajustar um pouco nossa última função:
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
Ótimo. Agora podemos usar nosso iterador. Aqui está um exemplo de todo o nosso código:
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
current = self.current
self.current += 1
return current
# Criar uma instância do iterador personalizado
my_iter = MyIterator(1, 5)
# Usamos o iterador para percorrer os elementos
for num in my_iter:
print(num)
5.3 Iterador correto
Por que o iterador do exemplo anterior é ruim? Sim, é um iterador, ele funciona, mas é muito primitivo. Não é possível percorrer a mesma coleção de elementos simultaneamente com diferentes iteradores.
Seria mais correto escrever um código que, no método __iter__
, não retorna uma referência para si mesmo, mas
sim um objeto separado,
que retornaria corretamente todos os elementos.
Exemplo:
class MyIterable:
def __init__(self, data):
self.data = data
def __iter__(self):
return MyIterator(self.data)
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
item = self.data[self.index]
self.index += 1
return item
# Uso
my_iterable = MyIterable([1, 2, 3, 4])
for item in my_iterable:
print(item)
Neste exemplo, temos duas classes — na primeira, passamos a coleção que vamos percorrer com o iterador. E
a segunda é o iterador, que retorna os elementos da coleção no método next()
. Ele também é bem simples,
mas é assim que você deve adicionar iteradores às suas classes.
GO TO FULL VERSION