Skip to content

Latest commit

 

History

History

06-iterators

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

README.md

Iterators (Iteradores)

Bem-vindo ao Módulo 6!

Introdução

Grande parte das estruturas de dados em Python, como listas, tuplas, conjuntos, dicionários e arquivos, pode ser percorrida elemento por elemento. Esse processo é conhecido como iteração.

Quando utilizamos um laço for, normalmente não pensamos em como ele obtém cada elemento da coleção. Entretanto, por trás desse mecanismo existe um conceito fundamental da linguagem: os iteradores (iterators).

Um iterador é um objeto capaz de fornecer seus elementos um de cada vez, permitindo percorrer uma coleção sem precisar carregar todos os seus elementos simultaneamente em memória.

Os iteradores são a base de diversas funcionalidades do Python, como laços de repetição, geradores (generators), compreensões (comprehensions), funções como map() e filter(), leitura de arquivos e processamento de grandes volumes de dados.

Neste capítulo estudaremos como funcionam os iteradores, o protocolo de iteração do Python, como criar iteradores personalizados e suas aplicações práticas.

Base Teórica

O que é um Iterador?

Um iterador é um objeto que fornece elementos de uma coleção um por vez, conforme eles são solicitados.

Visualmente:

Coleção

↓

Iterador

↓

Elemento 1

↓

Elemento 2

↓

Elemento 3

↓

Fim da coleção

Em vez de disponibilizar todos os elementos de uma só vez, o iterador entrega cada item sob demanda.

Iterável x Iterador

Esses dois conceitos costumam ser confundidos.

Iterável (Iterable)

É qualquer objeto que pode ser percorrido.

Exemplos:

  • Listas;
  • Tuplas;
  • Strings;
  • Conjuntos;
  • Dicionários;
  • Arquivos.

Exemplo:

nomes = ["Ana", "Carlos", "Pedro"]

A lista é um iterável.

Iterador (Iterator)

É o objeto responsável por percorrer o iterável.

Podemos obtê-lo utilizando a função iter().

nomes = ["Ana", "Carlos", "Pedro"]

iterador = iter(nomes)

print(iterador)

Obtendo os Elementos

Os elementos são obtidos utilizando next().

nomes = ["Ana", "Carlos", "Pedro"]

iterador = iter(nomes)

print(next(iterador))
print(next(iterador))
print(next(iterador))

Resultado:

Ana
Carlos
Pedro

O que acontece no final?

Quando todos os elementos são consumidos, ocorre uma exceção.

next(iterador)

Resultado:

StopIteration

Essa exceção informa que não existem mais elementos disponíveis.

Como o for funciona internamente

Quando escrevemos:

for numero in [1, 2, 3]:
    print(numero)

O Python faz algo semelhante a:

iterador = iter([1, 2, 3])

while True:

    try:
        numero = next(iterador)
        print(numero)

    except StopIteration:
        break

O laço for utiliza iteradores automaticamente.

O Protocolo de Iteração

Para que um objeto seja considerado um iterador, ele deve implementar dois métodos especiais:

  • __iter__()
  • __next__()

Método __iter__()

Retorna o próprio iterador.

def __iter__(self):
    return self

Método __next__()

Retorna o próximo elemento da sequência.

Quando não houver mais elementos, deve lançar:

StopIteration

Criando um Iterador Personalizado

class Contador:

    def __init__(self, limite):
        self.atual = 1
        self.limite = limite

    def __iter__(self):
        return self

    def __next__(self):

        if self.atual > self.limite:
            raise StopIteration

        numero = self.atual

        self.atual += 1

        return numero

Uso:

contador = Contador(5)

for numero in contador:
    print(numero)

Resultado:

1
2
3
4
5

Iteradores são Consumíveis

Após percorrer um iterador completamente, ele fica esgotado.

numeros = iter([1, 2, 3])

for n in numeros:
    print(n)

for n in numeros:
    print(n)

A segunda repetição não exibirá nada.

Verificando se um Objeto é Iterável

Podemos utilizar o módulo collections.abc.

from collections.abc import Iterable

print(isinstance([1, 2, 3], Iterable))
print(isinstance(10, Iterable))

Resultado:

True
False

Verificando se um Objeto é um Iterador

from collections.abc import Iterator

lista = [1, 2, 3]

iterador = iter(lista)

print(isinstance(lista, Iterator))

print(isinstance(iterador, Iterator))

Resultado:

False
True

Iteradores em Arquivos

Arquivos também são iteráveis.

with open("dados.txt") as arquivo:

    for linha in arquivo:
        print(linha.strip())

Cada linha é lida sob demanda, sem carregar todo o arquivo em memória.

Iteradores e Memória

Considere uma lista com um milhão de números.

numeros = list(range(1_000_000))

Todos os números ficam armazenados na memória.

Agora:

numeros = iter(range(1_000_000))

Os elementos são produzidos conforme necessário.

Esse comportamento reduz significativamente o consumo de memória.

Exemplo Completo

class Alfabeto:

    def __init__(self):
        self.letra = ord("A")

    def __iter__(self):
        return self

    def __next__(self):

        if self.letra > ord("Z"):
            raise StopIteration

        atual = chr(self.letra)

        self.letra += 1

        return atual


for letra in Alfabeto():
    print(letra)

Saída:

A
B
C
...
Z

Explicação

  1. A classe implementa o protocolo de iteração.
  2. __iter__() retorna o próprio objeto.
  3. __next__() fornece uma letra por vez.
  4. Ao ultrapassar "Z", lança StopIteration.
  5. O laço for consome automaticamente o iterador.

Iteradores vs Listas

Lista Iterador
Armazena todos os elementos Produz elementos sob demanda
Pode ser percorrida várias vezes Geralmente é consumido apenas uma vez
Maior consumo de memória Menor consumo de memória
Acesso direto por índice Apenas acesso sequencial

Erros Comuns

Confundir Iterável com Iterador

Errado:

lista = [1, 2, 3]

next(lista)

Resultado:

TypeError

Correto:

iterador = iter(lista)

next(iterador)

Esquecer de lançar StopIteration

Ao criar iteradores personalizados, o método __next__() deve lançar StopIteration quando não houver mais elementos.

Tentar reutilizar um iterador consumido

Após percorrer completamente um iterador, ele precisa ser recriado.

Implementar apenas __next__()

Um iterador deve implementar tanto __iter__() quanto __next__().

Boas Práticas

  • Utilize o laço for sempre que possível, pois ele gerencia os iteradores automaticamente.
  • Utilize next() apenas quando precisar controlar manualmente a iteração.
  • Crie iteradores personalizados apenas quando houver necessidade específica.
  • Prefira iteradores para processamento de grandes volumes de dados.
  • Lance corretamente StopIteration em implementações próprias.

Casos de Uso no Mercado

Processamento de Arquivos

Leitura de arquivos muito grandes linha por linha.

Engenharia de Dados

Processamento de milhões de registros sem carregar tudo na memória.

APIs

Consumo paginado de grandes conjuntos de dados.

Web Scraping

Processamento contínuo de páginas coletadas.

Ciência de Dados

Leitura incremental de conjuntos de dados extensos.

Streaming

Processamento de eventos em tempo real.

Comparação entre Iterable e Iterator

Iterable Iterator
Pode ser percorrido Percorre os elementos
Implementa __iter__() Implementa __iter__() e __next__()
Ex.: lista, tupla, string Ex.: resultado de iter(lista)
Pode gerar um novo iterador Mantém o estado da iteração

Resumo

Os iteradores são objetos responsáveis por percorrer coleções elemento por elemento, entregando os dados sob demanda. Eles seguem o protocolo de iteração, implementando os métodos __iter__() e __next__(), e são utilizados internamente pelo laço for e por diversas funcionalidades da linguagem. Compreender a diferença entre iteráveis e iteradores é fundamental para trabalhar com processamento eficiente de dados, especialmente em aplicações que manipulam grandes volumes de informações ou utilizam leitura incremental de arquivos e fluxos de dados.

Exercícios Propostos

  1. Utilize iter() e next() para percorrer manualmente uma lista de números.
  2. Crie um iterador para uma string e exiba seus caracteres um a um utilizando next().
  3. Reescreva um laço for utilizando explicitamente iter(), next() e try/except StopIteration.
  4. Implemente uma classe Contador que produza números de 1 até um limite informado.
  5. Crie um iterador que percorra os meses do ano.
  6. Desenvolva um iterador que gere apenas números pares até um valor máximo.
  7. Utilize collections.abc para verificar se diferentes objetos são iteráveis e/ou iteradores.
  8. Leia um arquivo de texto utilizando um iterador e conte quantas linhas ele possui.
  9. Crie um iterador personalizado que percorra uma lista de produtos retornando apenas seus nomes.
  10. Pesquise a relação entre iteradores e generators e explique por que geradores também são considerados iteradores.

Desafio

Desenvolva um programa chamado Sistema de Iteração Personalizada.

O sistema deverá implementar uma classe chamada Biblioteca, responsável por armazenar uma coleção de livros.

Cada livro deve possuir:

  • Título;
  • Autor;
  • Ano de publicação.

A classe Biblioteca deve implementar o protocolo de iteração para permitir percorrer os livros utilizando um laço for.

Funcionalidades

  1. Adicionar livros à biblioteca.
  2. Percorrer todos os livros utilizando um iterador personalizado.
  3. Exibir cada livro formatado durante a iteração.
  4. Permitir reiniciar a iteração criando um novo iterador.
  5. Tratar corretamente o fim da coleção utilizando StopIteration.

Requisitos

  • Implemente os métodos __iter__() e __next__().
  • Não utilize listas diretamente no laço principal; utilize o iterador personalizado.
  • Organize o código em classes e métodos reutilizáveis.
  • Documente os métodos utilizando docstrings.
  • Utilize tratamento de exceções quando necessário.

Desafio Extra

Implemente um segundo iterador para a biblioteca que permita percorrer os livros em ordem inversa, do mais recentemente adicionado para o primeiro, sem alterar a coleção original.

Esse desafio integra conceitos de iteradores, protocolo de iteração, programação orientada a objetos, tratamento de exceções, coleções, laços de repetição e organização de código, demonstrando como o mecanismo de iteração do Python funciona internamente e como ele pode ser personalizado para diferentes necessidades.