Vamos explorar o conceito de geradores em Python, uma ferramenta poderosa que permite iterar sobre sequências de maneira eficiente e económica em termos de memória. Os geradores são especialmente úteis quando estamos a lidar com grandes volumes de dados que não podem ser armazenados na memória de uma só vez.
O que são Geradores?
Geradores são um tipo especial de funções que devolvem um iterador. Em termos simples, os geradores são funções que lembram o seu estado (contexto) e produzem uma sequência de valores ao longo do tempo. Em vez de retornar todos os valores de uma vez, um gerador gera um valor de cada vez conforme solicitado.
Podemos criar um gerador de duas formas principais:
Funções com a palavra-chave yield
A palavra-chave yield
é o coração dos geradores. Ela faz com que uma função se comporte de forma diferente de uma função normal. Quando uma função encontra a palavra-chave yield, ela pára a sua execução e retorna o valor especificado. Na próxima vez que a função é chamada, ela continua a execução a partir do ponto onde parou.
Exemplo Prático
Vamos criar um gerador que gera os primeiros n
números quadrados:
def gerar_quadrados(n):
for i in range(n):
yield i ** 2
# Uso do gerador
quadrados = gerar_quadrados(5)
for numero in quadrados:
print(numero)
vamos obter este resultado
0
1
4
9
16
Neste exemplo, a função gerar_quadrados
utiliza a palavra-chave yield
para gerar quadrados de números de 0 a n-1
. A vantagem deste método é que ele só calcula e produz cada valor quando é pedido, economizando memória pois a sequência não . Em vez de ficar armazenado em memória toda a sequência (os 5 números)
Expressões geradoras (generator expressions)
As expressões geradoras em Python são uma forma concisa e elegante de criar iteradores. Elas são semelhantes às list comprehensions, mas em vez de criar uma lista completa na memória, geram os valores sob pedido, assim como os geradores. Isso torna-as mais eficientes em termos de memória, especialmente quando se trabalha com sequências grandes.
Exemplo Prático
# Expressão geradora
quadrados = (i ** 2 for i in range(5))
# Uso do gerador
for numero in quadrados:
print(numero)
vamos obter este resultado
0
1
4
9
16
O código cria um gerador que calcula os quadrados dos números de 0 a 4. Quando iteramos sobre esse gerador, os quadrados são calculados e impressos um por um.
Aplicações Reais de Geradores
1. Processamento de Grandes Ficheiros: Os geradores são extremamente úteis para processar grandes ficheiros de forma eficiente. Por exemplo, ler um ficheiro linha por linha em vez de carregar todo o ficheiro na memória:
def ler_grandes_ficheiros(ficheiro):
with open(ficheiro, 'r') as f:
for linha in f:
yield linha.strip()
# Uso do gerador
linhas = ler_grandes_ficheiros('grande_ficheiro.txt')
for linha in linhas:
print(linha)
2. Calculador Lazy de Sequências Infinitas: Geradores podem ser utilizados para criar sequências infinitas, como a sequência de números de Fibonacci, onde os números são gerados conforme necessário:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# Uso do gerador
fib = fibonacci()
for _ in range(10):
print(next(fib))
O uso dos geradores aqui permite calcular apenas os valores que necessitamos, sem desperdiçar recursos.
Vantagens de Usar Geradores em Python
1. Eficiência de Memória:
- Geração sob pedido: os geradores produzem valores apenas quando solicitados, evitando a criação de listas completas na memória. Isso é crucial para lidar com grandes conjuntos de dados ou sequências infinitas.
- Redução de consumo de memória: Ao gerar valores um a um, os geradores economizam significativamente a memória do seu programa.
2. Laziness (Avaliação Preguiçosa):
- Cálculos adiados: Os cálculos são realizados apenas quando o valor é necessário, o que pode melhorar o desempenho em situações onde muitos valores podem não ser utilizados.
- Processamento infinito: os geradores podem ser usados para criar sequências infinitas, como números Fibonacci, sem esgotar a memória.
3. Simplicidade:
- Sintaxe clara: A sintaxe dos geradores, com a palavra-chave yield, é intuitiva e fácil de aprender.
- Código mais conciso: Geradores podem tornar o código mais conciso e legível, especialmente quando comparado com ciclos tradicionais.
4. Flexibilidade:
- Iteração personalizada: os geradores permitem criar iteradores personalizados para diversos tipos de dados e algoritmos.
- Combinação com outras estruturas: Podem ser combinados com outras estruturas de controle, como for e while, para criar fluxos de dados complexos.
Resumo
Neste capítulo, explorámos os fundamentos dos geradores em Python. Vimos como criar geradores através de funções com a palavra-chave yield
e expressões geradoras. Investigámos também aplicações práticas de geradores, incluindo processamento de grandes ficheiros e a criação de sequências infinitas.
Questionário
-
Quais são as duas principais formas de criar um gerador em Python?
- a) Usando a palavra-chave
yield
e compreensão de listas. - b) Usando a palavra-chave
yield
e compreensão de geradores. - c) Usando compreensão de listas e funções lambda.
- d) Usando a palavra-chave
yield
e funções normais.
- a) Usando a palavra-chave
-
Qual é a principal vantagem de usar um gerador em vez de uma lista?
- a) Os geradores são mais rápidos.
- b) Os geradores permitem economizar memória ao gerar valores conforme necessário.
- c) Os geradores são mais fáceis de escrever.
- d) Os geradores têm melhor suporte multiplataforma.
-
O que acontece quando uma função com
yield
é chamada?- a) A função executa completamente e retorna todos os valores.
- b) A função retorna um objeto iterador.
- c) A função retorna imediatamente None.
- d) A função entra num loop infinito.
-
Como se cria uma compreensão de geradores?
- a) Usando parênteses em vez de parênteses retos em listas.
- b) Usando parênteses retos em vez de parênteses em listas.
- c) Usando chaves em vez de parênteses em dicionários.
- d) Usando comprimentos diferentes de listas.