Geradores em Python
Este artigo explica os geradores em Python.
YouTube Video
Geradores em Python
Visão geral
Os geradores em Python são um tipo de iterador e um recurso poderoso para realizar processamentos repetitivos de forma eficiente. Eles permitem escrever códigos eficientes em memória ao lidar com grandes volumes de dados.
O que é um gerador?
Um gerador em Python é uma função especial que produz um valor de cada vez, definida usando a palavra-chave yield. Sua característica é pausar a execução enquanto mantém seu estado e pode ser retomada posteriormente.
Noções básicas do yield
yield é uma palavra-chave que retorna um valor e pausa a execução da função ao mesmo tempo.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8print(next(gen)) # 1
9print(next(gen)) # 2
10print(next(gen)) # 3- Quando chamada, esta função retorna um objeto gerador que fornece valores um por um.
- Se você chamar
next()quando não houver próximo valor, ocorrerá um erroStopIteration.
next() e StopIteration
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6gen = simple_generator()
7
8try:
9 while True:
10 value = next(gen)
11 print(value)
12except StopIteration:
13 print("Finished")- Ao tratar explicitamente o erro
StopIterationdesta forma, você pode detectar quando um gerador terminou.
send(value)
Chamar send(value) retoma o gerador e envia value para a posição da expressão yield. O valor enviado pode ser recebido do lado do gerador como valor de retorno da expressão yield. Na primeira chamada, não é possível enviar nada além de None com send(value), portanto, você deve usar next() ou send(None).
1def gen():
2 x = yield 1
3 print(f"x = {x}")
4 y = yield 2
5 print(f"y = {y}")
6
7g = gen()
8print(next(g)) # -> 1 (value from yield 1)
9print(g.send(10)) # -> x = 10, 2 (value from yield 2)
10print(g.send(20)) # -> y = 20, StopIteration occurs- Com
send(10), oyielddo gerador se torna uma expressão que retorna 10, e 10 é atribuído ax.
throw()
Chamar throw retoma o gerador e lança uma exceção na posição do yield pausado. Você pode tratar a exceção dentro do gerador para continuar o processamento. Se a exceção não for capturada, ela se propaga para fora e o gerador é encerrado.
1def gen():
2 try:
3 yield 1
4 except ValueError as e:
5 print(f"Caught: {e}")
6 yield "recovered"
7
8g = gen()
9print(next(g)) # -> 1
10print(g.throw(ValueError("boom"))) # -> Caught: boom, "recovered"- Neste código,
throwé chamado para injetar uma exceção no gerador. Do lado do gerador, a exceção é tratada erecoveredé retornado.
close()
Chamar close() encerra o gerador. Dentro do gerador, você pode executar a limpeza usando finally. Chamar next() ou send() após chamar close() levanta um erro StopIteration.
1def gen():
2 try:
3 yield 1
4 finally:
5 print("Cleaning up...")
6
7g = gen()
8print(next(g)) # -> 1
9g.close() # -> Cleaning up...- Este código mostra que chamar
close()encerra o gerador e dispara o processo de limpeza emfinally.
yield from
yield from é uma sintaxe usada para delegar a um subgerador. É uma maneira simples de chamar outro gerador dentro de um gerador e passar todos os seus valores para o escopo externo.
1def sub_gen():
2 yield 1
3 yield 2
4
5def main_gen():
6 yield from sub_gen()
7 yield 3
8
9print(list(main_gen())) # -> [1, 2, 3]- Este código delega todos os valores do subgerador ao gerador externo usando
yield frome então produz3.
Relação com iteradores
Geradores implementam internamente __iter__() e __next__(), tornando-os um tipo de iterador. Portanto, eles são totalmente compatíveis com operações iteráveis como loops for.
Integração com loops for
Em Python, um loop for usa internamente o next() para recuperar valores automaticamente.
1def simple_generator():
2 yield 1
3 yield 2
4 yield 3
5
6for value in simple_generator():
7 print(value)Dessa forma, o tratamento de StopIteration também é automático.
Criando geradores infinitos
1def count_up(start=0):
2 while True:
3 yield start
4 start += 1
5
6counter = count_up()
7print(next(counter)) # 0
8print(next(counter)) # 1É possível criar laços infinitos, mas deve-se ter cuidado ao utilizá-los.
Expressões de gerador
Expressões de gerador, escritas com parênteses, permitem definir geradores com sintaxe semelhante às compreensões de listas.
1# List comprehension (generates the entire list at once)
2squares_list = [x**2 for x in range(5)]
3print(squares_list)
4
5# Generator expression
6squares_gen = (x**2 for x in range(5))
7for square in squares_gen:
8 print(square)Ao contrário das compreensões de listas, elas não carregam todos os elementos na memória de uma só vez, tornando-se mais eficientes em termos de memória.
Tratamento de erros em geradores
Exceções podem ocorrer dentro de um gerador. Nesses casos, você usa o try-except como em um código Python normal.
1def safe_divide_generator(numbers, divisor):
2 """Yields results of dividing numbers by a given divisor safely."""
3 for number in numbers:
4 try:
5 yield number / divisor # Attempt to divide and yield result.
6 except ZeroDivisionError:
7 yield float('inf') # Return infinity if division by zero occurs.
8
9# Example usage
10numbers = [10, 20, 30]
11gen = safe_divide_generator(numbers, 0) # Create generator with divisor as 0.
12for value in gen:
13 print(value) # Output: inf, inf, infNeste exemplo, o tratamento adequado de erros é realizado no caso de uma divisão por zero.
Rastreamento de pilha de um gerador
Se uma exceção ocorrer dentro do gerador, ela será lançada quando o gerador for retomado.
1def error_generator():
2 """A generator that yields values and raises an error."""
3 yield 1
4 raise ValueError("An error occurred") # Raise a ValueError intentionally.
5 yield 2
6
7gen = error_generator()
8print(next(gen)) # Output: 1 (first value yielded)
9try:
10 print(next(gen)) # Attempt to get the next value, which raises an error
11except ValueError as e:
12 print(e) # Output: An error occurred (exception message is printed)- Este gerador retorna
1primeiro. O erro lançado ao retomar é capturado e exibido como uma mensagem de erro.
Exemplos de uso de geradores
Lendo um arquivo linha por linha (adequado para arquivos grandes)
1def read_large_file(filepath):
2 with open(filepath, 'r') as f:
3 for line in f:
4 yield line.strip()- Esta função lê um arquivo de texto linha por linha usando um iterador, remove os espaços em branco de cada linha e retorna como um gerador, permitindo que arquivos grandes sejam processados com baixo uso de memória.
Gerador para a sequência de Fibonacci
1def fibonacci(limit):
2 a, b = 0, 1
3 while a < limit:
4 yield a
5 a, b = b, a + b
6
7for n in fibonacci(100):
8 print(n)- Este código usa um gerador para gerar sequencialmente números de Fibonacci menores que o limite superior e os imprime usando um laço
for.
Casos de uso
Os geradores também podem ser usados nos seguintes cenários.
- Processamento sequencial de arquivos CSV ou de log grandes
- Paginação de API
- Processamento de dados em streaming (por exemplo, Kafka, dispositivos IoT)
Resumo
| Conceito | Ponto chave |
|---|---|
yield |
Pausa e retorna um valor |
| Função geradora | Uma função que contém yield e retorna um iterador quando chamada |
| Vantagens | Eficiente em memória e ideal para processar grandes conjuntos de dados |
| Expressão de gerador | Permite uma sintaxe concisa como (x for x in iterable) |
Ao usar geradores, você pode processar grandes conjuntos de dados de forma eficiente, economizando memória e mantendo seu código conciso.
Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.