
Passo 1 de 7
Conectar a classe ao protocolo de iteração
Veja como uma classe passa a participar de iter, next e for ao oferecer os métodos especiais do protocolo de iteração.
Trilha de aprendizado · Nível 10 · Tutorial 2
Criar classes compatíveis com a iteração do Python, separando os dados da fonte do estado de cada percurso quando forem necessárias passagens independentes.
Conectar a classe ao protocolo de iteração
Veja como uma classe passa a participar de iter, next e for ao oferecer os métodos especiais do protocolo de iteração. 2 min
Implementar um iterador finito
Construa um iterador que produz números inteiros de um início inclusivo até um fim exclusivo, mantendo sua própria posição de consumo. 4 min
Preservar o esgotamento e tratar os limites
Verifique os casos de limite de um iterador finito e preserve seu estado após o término. 3 min
Separar a fonte de cada percurso
Crie uma fonte reutilizável que guarda apenas seus limites e entrega um novo iterador para cada passagem. 3 min
Comprovar a independência dos percursos
Observe dois iteradores criados pela mesma fonte e confirme que cada um mantém sua própria posição de consumo. 2 min
Testar o comportamento pela interface pública
Use pytest para conferir valores, identidade e término das classes Faixa e IteradorFaixa sem acessar seus atributos internos. 3 min
Aplicação final: corrigir e validar as duas classes
Corrija uma implementação com estado compartilhado e reinicialização indevida, depois valide os contratos públicos de iteração. 4 min

Passo 1 de 7
Veja como uma classe passa a participar de iter, next e for ao oferecer os métodos especiais do protocolo de iteração.
Você já consumiu iteradores com iter, next e for. Agora a classe implementará o comportamento acionado por essas operações.
Quando Python executa iter(objeto), ele usa objeto.__iter__(). Quando executa next(iterador), usa iterador.__next__(). Em geral, escrevemos e consumimos pela interface pública — iter, next e for — sem chamar esses métodos especiais diretamente.
A imagem mostra o caminho entre as operações públicas e os contratos que sua classe deve cumprir.

iter obtém o iterador; next pede um valor; for repete esse processo até o término.
__iter__ deve devolver um objeto iterador. Não basta devolver uma lista, uma tupla ou qualquer coleção de valores: o resultado precisa poder atender a chamadas de next(...).
__next__ deve devolver um elemento por chamada. Quando não existir outro elemento, deve lançar StopIteration. Esse sinal não é um valor comum: ele informa ao consumidor que o percurso acabou.
Exemplo
fonte = MinhaClasse()
iterador = iter(fonte) # aciona fonte.__iter__()
valor = next(iterador) # aciona iterador.__next__()
for valor in fonte:
print(valor)No for, Python obtém um iterador e solicita valores sucessivos. Ao receber StopIteration, o laço termina normalmente.
Dica
Por enquanto, concentre-se nos contratos: quem fornece um iterador e quem fornece um próximo valor. A posição de avanço e a separação entre fonte e percurso aparecerão nos próximos passos.
Relacione cada elemento à sua responsabilidade.
Toque em um item e depois no par correspondente.

Passo 2 de 7
Construa um iterador que produz números inteiros de um início inclusivo até um fim exclusivo, mantendo sua própria posição de consumo.
Vamos criar IteradorFaixa, uma classe que produz inteiros de inicio até antes de fim. Por exemplo, IteradorFaixa(3, 6) entrega 3, 4 e 5.
Cada instância guarda somente o que precisa para avançar: a posição atual e o limite. Os resultados não são montados previamente em uma lista.
A posição começa em 3. A cada valor entregue, ela aumenta uma unidade. Quando chega a 6, não há mais valor válido para produzir.

Início inclusivo: 3. Fim exclusivo: 6. A posição é o estado que muda em cada avanço.
Dica
Duas instâncias de IteradorFaixa podem ter posições diferentes. Nesta etapa, cada objeto representa um único percurso de consumo.
No iterador, __iter__ devolve o próprio objeto: self. Isso não reinicia nem avança a posição.
Em __next__, teste primeiro se a posição chegou ao limite. Se chegou, lance StopIteration. Caso contrário, guarde a posição atual, avance-a e devolva o valor guardado.
Crie um arquivo Python com esta classe e execute os exemplos abaixo no seu computador.
class IteradorFaixa:
def __init__(self, inicio, fim):
self.atual = inicio
self.fim = fim
def __iter__(self):
return self
def __next__(self):
if self.atual >= self.fim:
raise StopIteration
valor = self.atual
self.atual += 1
return valor
it = IteradorFaixa(3, 6)
print(next(it)) # 3
print(next(it)) # 4
print(next(it)) # 5
print(next(it)) # StopIterationExemplo
Com atual = 3, __next__ guarda 3 em valor, altera atual para 4 e devolve 3. Assim, o primeiro número não é pulado.
Na próxima chamada, o mesmo processo devolve 4 e deixa a posição em 5.
Para uma posição ainda válida, organize as ações de __next__ na ordem correta.
Execute o código da tela anterior. Para IteradorFaixa(3, 6), relate a sequência devolvida pelas três primeiras chamadas a next, como a posição muda e o que ocorre na chamada seguinte.
Escreva pelo menos 80 caracteres (0/80).

Passo 3 de 7
Verifique os casos de limite de um iterador finito e preserve seu estado após o término.
Em IteradorFaixa(inicio, fim), o fim é exclusivo. Portanto, a condição self.atual >= self.fim significa que não existe próximo valor.
inicio >= fim, o percurso já nasce vazio.atual ainda é menor que fim, esse valor é entregue — inclusive o último válido.atual avança, as próximas chamadas continuam encontrando a condição de término.
O valor no limite não é produzido. Com início igual ou maior que o fim, não há primeiro valor.
A verificação ocorre antes de devolver um valor. __iter__ apenas confirma que este objeto é o iterador; ele não altera atual.
class IteradorFaixa:
def __init__(self, inicio, fim):
self.atual = inicio
self.fim = fim
def __iter__(self):
return self
def __next__(self):
if self.atual >= self.fim:
raise StopIteration
valor = self.atual
self.atual += 1
return valorExemplo
it = IteradorFaixa(2, 5)
print(next(it)) # 2
print(next(it)) # 3
print(next(it)) # 4: último valor válido
try:
next(it)
except StopIteration:
print("terminou")
print(iter(it) is it) # True
next(it) # ainda lança StopIterationApós produzir 4, a posição passa a ser 5. Como 5 >= 5, o iterador permanece esgotado.
Atenção
Evite atribuir self.atual = ... dentro de __iter__. Uma chamada a iter(it) sobre o mesmo iterador não deve restaurar valores já consumidos. Além disso, __next__ deve usar raise StopIteration: retornar None ou retornar um objeto StopIteration faria esse objeto ser tratado como um valor normal.
Considere it = IteradorFaixa(8, 8). Qual deve ser o resultado de next(it)?
Qual trecho mantém o esgotamento de um iterador e informa corretamente que não há próximo valor?

Passo 4 de 7
Crie uma fonte reutilizável que guarda apenas seus limites e entrega um novo iterador para cada passagem.
Até aqui, IteradorFaixa guarda os limites e a posição que avança. Para permitir que a mesma faixa seja percorrida novamente, crie uma fonte chamada Faixa.
Faixa conserva somente inicio e fim.IteradorFaixa conserva sua própria atual e a atualiza em __next__.Assim, a fonte descreve os valores possíveis; o iterador representa um percurso em andamento.
A mesma fonte pode criar percursos separados com os mesmos limites.

Faixa(2, 5) não guarda a posição. Cada iterador recebido começa com sua própria posição em 2.
Dica
Se um atributo muda a cada next, ele pertence ao iterador do percurso — não à fonte reutilizável.
Defina IteradorFaixa e Faixa no mesmo arquivo.
class IteradorFaixa:
def __init__(self, inicio, fim):
self.atual = inicio
self.fim = fim
def __iter__(self):
return self
def __next__(self):
if self.atual >= self.fim:
raise StopIteration
valor = self.atual
self.atual += 1
return valor
class Faixa:
def __init__(self, inicio, fim):
self.inicio = inicio
self.fim = fim
def __iter__(self):
return IteradorFaixa(self.inicio, self.fim)
faixa = Faixa(3, 6)
print(list(faixa))
print(list(faixa))Em IteradorFaixa, __iter__ devolve self: aquele objeto já é o percurso e não deve reiniciar.
Em Faixa, __iter__ cria IteradorFaixa(self.inicio, self.fim): cada solicitação recebe um percurso novo. Faixa não implementa __next__, pois ela não avança nem produz valores diretamente.
Exemplo
Ao executar o código localmente, as duas linhas exibem:
[3, 4, 5]
[3, 4, 5]O primeiro list esgota apenas o iterador criado para aquela passagem. O segundo list pede outro iterador à mesma Faixa.
Associe cada elemento à classe que deve ser responsável por ele.
Toque em um item e depois no par correspondente.
Execute o código da tela anterior. O que as duas chamadas a list(faixa) exibiram? Explique, em uma frase, por que a segunda passagem não ficou vazia.
Escreva pelo menos 30 caracteres (0/30).

Passo 5 de 7
Observe dois iteradores criados pela mesma fonte e confirme que cada um mantém sua própria posição de consumo.
Uma instância de Faixa guarda apenas os limites. Ao chamar iter(faixa) duas vezes, você recebe dois objetos IteradorFaixa distintos, ambos começando no mesmo início.
Assim, avançar um deles não altera a posição do outro. Não é necessário criar ou duplicar uma lista de valores: cada iterador conserva somente sua própria posição.
A mesma fonte fornece dois iteradores com marcadores de posição separados.

A fonte compartilha os limites; cada percurso tem sua própria posição.
Execute este trecho depois de definir as classes Faixa e IteradorFaixa criadas anteriormente.
faixa = Faixa(2, 5)
percurso_a = iter(faixa)
percurso_b = iter(faixa)
print(percurso_a is percurso_b) # False
print(next(percurso_a)) # 2
print(next(percurso_a)) # 3
print(next(percurso_b)) # 2
print(list(percurso_a)) # [4]
print(list(percurso_b)) # [3, 4]No código anterior, percurso_a chega ao fim após entregar 4. Isso não esgota percurso_b: ele havia entregado apenas 2 e ainda tem valores próprios para fornecer.
Complete o resultado:
list(percurso_b) produz ___
A fonte deixa de ser reutilizável se ela criar um iterador uma única vez e devolvê-lo sempre. Nesse caso, duas chamadas a iter(faixa) retornam o mesmo objeto; os consumidores passam a compartilhar a mesma posição.
A versão incorreta reutiliza o percurso armazenado. A correta cria outro a cada chamada.
# Incorreta: todos recebem o mesmo iterador
class FaixaComDefeito:
def __init__(self, inicio, fim):
self.inicio = inicio
self.fim = fim
self._iterador = IteradorFaixa(inicio, fim)
def __iter__(self):
return self._iterador
# Correta: cada chamada inicia um percurso independente
class Faixa:
def __init__(self, inicio, fim):
self.inicio = inicio
self.fim = fim
def __iter__(self):
return IteradorFaixa(self.inicio, self.fim)Suponha que FaixaComDefeito.__iter__ seja usada. Explique o efeito de criar a = iter(faixa) e b = iter(faixa), avançar a até o fim e depois tentar consumir b. Indique a correção.
Escreva pelo menos 80 caracteres (0/80).

Passo 6 de 7
Use pytest para conferir valores, identidade e término das classes Faixa e IteradorFaixa sem acessar seus atributos internos.
Os testes devem consumir as classes como o código cliente faria: com iter(), next(), list() e for. Assim, eles continuam válidos mesmo que você altere os nomes ou a forma de guardar a posição internamente.
Verifique três tipos de resultado: valores produzidos, identidade do iterador e exceções no término.

A fonte pode criar novos percursos; cada percurso é consumido apenas por operações públicas como iter e next.
Dica
Não faça asserções como assert iterador.atual == 3. A posição é um detalhe de implementação. Prefira pedir o próximo valor e conferir o resultado: assert next(iterador) == 3.
No seu projeto, crie o arquivo faixa.py com as duas classes abaixo. A fonte Faixa guarda apenas os limites; cada chamada a iter(faixa) cria um novo IteradorFaixa.
class IteradorFaixa:
def __init__(self, inicio, fim):
self.atual = inicio
self.fim = fim
def __iter__(self):
return self
def __next__(self):
if self.atual >= self.fim:
raise StopIteration
valor = self.atual
self.atual += 1
return valor
class Faixa:
def __init__(self, inicio, fim):
self.inicio = inicio
self.fim = fim
def __iter__(self):
return IteradorFaixa(self.inicio, self.fim)Crie test_faixa.py na mesma pasta. Os testes abaixo cobrem a sequência completa, as fontes vazias, o esgotamento persistente, a identidade do iterador e a independência entre percursos.
import pytest
from faixa import Faixa
def test_produz_sequencia_completa_na_ordem():
assert list(Faixa(2, 5)) == [2, 3, 4]
@pytest.mark.parametrize(("inicio", "fim"), [(4, 4), (5, 2)])
def test_fonte_vazia_nao_produz_valores(inicio, fim):
assert list(Faixa(inicio, fim)) == []
def test_iterador_mantem_identidade_e_posicao():
iterador = iter(Faixa(2, 5))
assert iter(iterador) is iterador
assert next(iterador) == 2
assert next(iter(iterador)) == 3
def test_esgotamento_permanece_apos_iter():
iterador = iter(Faixa(7, 8))
assert next(iterador) == 7
with pytest.raises(StopIteration):
next(iterador)
with pytest.raises(StopIteration):
next(iter(iterador))
def test_duas_passagens_completas_da_mesma_fonte():
faixa = Faixa(1, 4)
assert list(faixa) == [1, 2, 3]
assert list(faixa) == [1, 2, 3]
def test_iteradores_da_mesma_fonte_sao_independentes():
faixa = Faixa(10, 13)
primeiro = iter(faixa)
segundo = iter(faixa)
assert primeiro is not segundo
assert next(primeiro) == 10
assert next(primeiro) == 11
assert next(segundo) == 10
assert list(primeiro) == [12]
assert list(segundo) == [11, 12]Exemplo
No terminal, dentro da pasta que contém os dois arquivos, instale pytest caso ainda não esteja disponível:
python -m pip install pytest
Depois, execute a suíte:
python -m pytest -v
Com o código apresentado, o resultado esperado é a aprovação de todos os 7 testes.
Associe o cenário à garantia principal verificada.
Toque em um item e depois no par correspondente.
Execute os testes no seu computador. Quantos testes passaram? Se houve falha, qual comportamento ela indicou?
Escreva pelo menos 60 caracteres (0/60).

Passo 7 de 7
Corrija uma implementação com estado compartilhado e reinicialização indevida, depois valide os contratos públicos de iteração.
A implementação abaixo parece produzir números, mas viola dois contratos: IteradorFaixa.__iter__ reinicia um percurso já iniciado, e Faixa.__iter__ devolve sempre o mesmo percurso. Leia o código e localize as duas linhas que causam esses comportamentos.
A correção deve ser localizada: a fonte guarda apenas inicio e fim; cada iterador guarda sua própria atual.
Compare o desenho com o código defeituoso: dois chamados a iter(faixa) devem criar dois objetos de percurso, nunca compartilhar um marcador de posição.

A fonte não consome valores; cada iterador controla somente a posição do seu próprio percurso.
Crie um arquivo faixa.py com este código. Não altere __next__: o avanço e o término já estão no lugar certo.
class IteradorFaixa:
def __init__(self, inicio, fim):
self.atual = inicio
self.fim = fim
def __iter__(self):
self.atual = 0 # falha: reinicia o percurso
return self
def __next__(self):
if self.atual >= self.fim:
raise StopIteration
valor = self.atual
self.atual += 1
return valor
class Faixa:
def __init__(self, inicio, fim):
self.inicio = inicio
self.fim = fim
self._iterador = IteradorFaixa(inicio, fim)
def __iter__(self):
return self._iterador # falha: compartilha o percursoNo seu computador, corrija faixa.py para que:
IteradorFaixa.__iter__ apenas devolva o próprio objeto, sem mudar atual.Faixa.__iter__ crie e devolva um novo IteradorFaixa usando os limites da fonte.Em seguida, salve o teste abaixo como test_faixa.py na mesma pasta e execute pytest -q.
Os testes observam valores, identidade e exceções por iter, next e list, sem consultar atributos internos.
import pytest
from faixa import Faixa, IteradorFaixa
def test_produz_sequencia_completa_na_ordem():
assert list(Faixa(2, 5)) == [2, 3, 4]
@pytest.mark.parametrize("inicio, fim", [(4, 4), (5, 2)])
def test_fonte_vazia(inicio, fim):
assert list(Faixa(inicio, fim)) == []
def test_iterador_permanece_esgotado_e_iter_nao_reinicia():
percurso = iter(Faixa(1, 3))
assert iter(percurso) is percurso
assert next(percurso) == 1
assert iter(percurso) is percurso
assert next(percurso) == 2
with pytest.raises(StopIteration):
next(percurso)
with pytest.raises(StopIteration):
next(percurso)
def test_passagens_da_mesma_fonte_sao_independentes():
faixa = Faixa(10, 13)
primeiro = iter(faixa)
segundo = iter(faixa)
assert primeiro is not segundo
assert next(primeiro) == 10
assert next(primeiro) == 11
assert next(segundo) == 10
assert list(primeiro) == [12]
assert list(segundo) == [11, 12]
def test_iterador_vazio_termina_imediatamente():
percurso = IteradorFaixa(3, 3)
with pytest.raises(StopIteration):
next(percurso)Compare com sua versão depois de tentar corrigir e executar os testes. Observe que cada __iter__ tem uma responsabilidade diferente.
class IteradorFaixa:
def __init__(self, inicio, fim):
self.atual = inicio
self.fim = fim
def __iter__(self):
return self
def __next__(self):
if self.atual >= self.fim:
raise StopIteration
valor = self.atual
self.atual += 1
return valor
class Faixa:
def __init__(self, inicio, fim):
self.inicio = inicio
self.fim = fim
def __iter__(self):
return IteradorFaixa(self.inicio, self.fim)Após executar os testes, explique quais mudanças você fez e por que elas garantem, ao mesmo tempo, percursos independentes e esgotamento persistente.
Escreva pelo menos 80 caracteres (0/80).
Resumo
Você integrou uma fonte reutilizável a iteradores finitos e verificou o resultado somente pela interface pública.
Faixa conserva os parâmetros estáveis inicio e fim; ela não conserva uma posição de consumo.iter(faixa) cria um novo IteradorFaixa, permitindo passagens completas e simultâneas independentes.IteradorFaixa.__iter__ devolve self sem avançar nem reiniciar o estado.__next__ entrega o valor atual, avança uma unidade e lança StopIteration ao atingir o limite.StopIteration; ele não devolve None como se fosse um valor.Parabéns! Você concluiu: Implementar iteráveis e iteradores em classes
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks