Geradores em Python: Conceitos e Implementações

Em Python, os geradores são uma ferramenta poderosa para criar iteradores de forma concisa, utilizando a palavra-chave yield. Esta abordagem simplifica a construção de sequências preguiçosas, que produzem valores sob demanda.

Para ilustrar, definimos uma função geradora básica:

def criar_gerador():
    print("Executando o gerador")
    yield 'u'
    yield 'v'
    yield 'w'

gen = criar_gerador()
print(type(gen))  # Saída: <class 'generator'>

Este gerador pode ser iterado diretamente em um laço for:

for caractere in criar_gerador():
    print(caractere)
# Saída:
# Executando o gerador
# u
# v
# w

Os geradores mantêm seu estado entre as chamadas de yield. Considere uma implementação similar a uma classe contadora:

def gerador_intervalo(valor_inicial, valor_final):
    atual = valor_inicial
    while atual <= valor_final:
        yield atual
        atual += 1

for num in gerador_intervalo(3, 7):
    print(num, end=' ')
# Saída: 3 4 5 6 7

Ao inspecionar um objeto gerador com dir(), métodos como __iter__ e __next__ estarão presentes, confirmando seu protocolo de iteração.

Uma aplicação comum é o processamento eficeinte de grandes volumes de daddos, como na varredura de diretórios com os.walk(), que atua como um gerador para economizar memória.

Geradores podem produzir sequências infinitas:

def gerador_infinito(inicio=0):
    valor = inicio
    while True:
        yield valor
        valor += 1

contador = gerador_infinito(2)
for _ in range(5):
    print(next(contador), end=' ')
# Saída: 2 3 4 5 6

Uma limitação importante é que gerdaores não são reutilizáveis após o consumo. No entanto, é possível criar objetos iteráveis usando classes que implementam o método __iter__:

class IntervaloIteravel:
    def __init__(self, minimo, maximo):
        self.minimo = minimo
        self.maximo = maximo

    def __iter__(self):
        corrente = self.minimo
        while corrente <= self.maximo:
            yield corrente
            corrente += 1

intervalo = IntervaloIteravel(4, 9)
for val in intervalo:
    print(val, end=' ')
# Saída: 4 5 6 7 8 9
# A iteração pode ser repetida, pois __iter__ retorna um novo gerador a cada chamada

Diferentemente, para transformar uma classe em um iterador completo, deve-se implementar tanto __iter__ quanto __next__:

from collections.abc import Iterator

class SequenciaLimitada:
    def __init__(self, comeco, limite):
        self.atual = comeco
        self.limite = limite

    def __iter__(self):
        return self

    def __next__(self):
        if self.atual > self.limite:
            raise StopIteration
        valor = self.atual
        self.atual += 1
        return valor

seq = SequenciaLimitada(10, 13)
print(isinstance(seq, Iterator))  # Saída: True
for elemento in seq:
    print(elemento, end=' ')
# Saída: 10 11 12 13

Tags: Python geradores iteradores yield collections.abc.Iterator

Publicado em 6-30 23:00