Introdução
Neste laboratório, aprenderemos sobre Iteradores, Geradores e Expressões Geradoras (Generator Expressions) embutidos no Python. Veremos como essas construções podem ser usadas para escrever código eficiente e elegante em Python.
Conquistas
- Iterador (Iterator)
- Gerador (Generator)
- Expressão Geradora (Generator Expression)
Iterador
Um iterador é um objeto que pode ser iterado (percorrido em loop). Um objeto que retornará dados, um elemento por vez. Em Python, um iterador é criado a partir de um objeto iterável, como uma lista, tupla ou string.
Abra um novo interpretador Python.
python3
Para criar um iterador em Python, precisamos implementar dois métodos em nosso objeto: __iter__ e __next__.
__iter__ retorna o próprio objeto iterador. O método __next__ retorna o próximo valor do iterador. Se não houver mais itens para retornar, ele deve levantar StopIteration.
Aqui está um exemplo de um iterador simples que itera sobre uma lista de números:
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
## len() is the number of elements in the list
if self.index >= len(self.data):
raise StopIteration
result = self.data[self.index]
self.index += 1
return result
iterator = MyIterator([1, 2, 3, 4, 5])
for x in iterator:
print(x)
Saída:
1
2
3
4
5
Iteradores são úteis porque nos permitem acessar os elementos de um iterável um de cada vez, em vez de carregar todos os elementos na memória de uma vez. Isso pode ser especialmente útil ao trabalhar com grandes conjuntos de dados que não cabem na memória.
Iteradores também são usados para implementar avaliação preguiçosa (lazy evaluation) em Python. Isso significa que os elementos de um iterador são gerados apenas quando são necessários, em vez de gerar todos os elementos antecipadamente. Essa pode ser uma abordagem mais eficiente, pois nos permite evitar a geração e o armazenamento de elementos desnecessários.
Se você deseja obter um elemento por vez de um iterador, pode usar a função next(). Essa função retornará o próximo elemento do iterador. Se não houver mais elementos, ela levantará uma exceção StopIteration.
iterator = MyIterator([1, 2, 3, 4, 5])
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
## StopIteration
print(next(iterator))
Saída:
1
2
3
4
## StopIteration
Aqui estão alguns casos de uso comuns para iteradores em Python:
- Percorrer os elementos de um grande conjunto de dados, um elemento por vez.
- Implementar avaliação preguiçosa de um grande conjunto de dados.
- Implementar lógica de iteração personalizada em uma classe.
- Iteradores são uma ferramenta poderosa em Python e podem ser usados para escrever código eficiente e elegante.
Gerador
Um gerador é um tipo especial de iterador que é criado usando uma função. É uma maneira simples de criar um iterador usando uma função.
Uma função geradora (generator function) é definida como uma função regular, mas em vez de usar a palavra-chave return para retornar um valor, ela usa a palavra-chave yield. Quando a função geradora é chamada, ela não executa o corpo da função imediatamente. Em vez disso, ela retorna um objeto gerador que pode ser usado para executar o corpo da função sob demanda.
A função geradora pode ter uma instrução yield em qualquer lugar em seu corpo. Quando a função geradora é chamada, ela não executa o corpo da função imediatamente. Em vez disso, ela retorna um objeto gerador que pode ser usado para executar o corpo da função sob demanda.
Aqui está um exemplo de uma função geradora que gera os quadrados de uma lista de números:
def my_generator(data):
for x in data:
yield x**2
for x in my_generator([1, 2, 3, 4, 5]):
print(x)
Saída:
1
4
9
16
25
Geradores são úteis porque nos permitem gerar elementos sob demanda, em vez de gerar todos os elementos antecipadamente. Essa pode ser uma abordagem mais eficiente, pois nos permite evitar a geração e o armazenamento de elementos desnecessários.
Geradores também são usados para implementar avaliação preguiçosa (lazy evaluation) em Python. Isso significa que os elementos de um gerador são gerados apenas quando são necessários, em vez de gerar todos os elementos antecipadamente. Essa pode ser uma abordagem mais eficiente, pois nos permite evitar a geração e o armazenamento de elementos desnecessários.
Aqui estão alguns casos de uso comuns para geradores em Python:
- Gerar elementos sob demanda, em vez de gerar todos os elementos antecipadamente.
- Implementar avaliação preguiçosa de um grande conjunto de dados.
- Implementar lógica de iteração personalizada em uma função.
- Geradores são uma ferramenta poderosa em Python e podem ser usados para escrever código eficiente e elegante.
Diferenças entre Iterador e Gerador
A principal diferença entre um iterador e um gerador é a maneira como eles são implementados.
Um iterador é um objeto que implementa dois métodos: __iter__ e __next__. O método __iter__ retorna o próprio objeto iterador, e o método __next__ retorna o próximo valor do iterador.
Um gerador é uma função que usa a palavra-chave yield para retornar um valor. Quando a função geradora é chamada, ela não executa o corpo da função imediatamente. Em vez disso, ela retorna um objeto gerador que pode ser usado para executar o corpo da função sob demanda.
Aqui está um resumo das principais diferenças entre iteradores e geradores:
- Iteradores são objetos que implementam os métodos
__iter__e__next__. Eles são criados a partir de objetos iteráveis, como listas, tuplas ou strings. - Geradores são funções que usam a palavra-chave
yieldpara retornar um valor. Eles são criados chamando uma função geradora. - Iteradores podem ser implementados usando uma classe, enquanto geradores são implementados usando uma função.
- Iteradores retornam um elemento por vez, enquanto geradores retornam um objeto gerador que pode ser usado para gerar elementos sob demanda.
- Iteradores são usados para acessar os elementos de um objeto iterável um de cada vez, enquanto geradores são usados para gerar elementos sob demanda.
No geral, tanto iteradores quanto geradores são ferramentas úteis para iterar sobre uma sequência de elementos em Python. Eles nos permitem acessar ou gerar os elementos de uma sequência um de cada vez, o que pode ser mais eficiente do que gerar todos os elementos antecipadamente.
Exemplo Avançado: Gerador de Números Primos
Neste exemplo, criaremos um gerador que gera números primos.
Primeiro, vamos definir uma função auxiliar _is_prime que retorna True se um número é primo e False caso contrário:
def _is_prime(n):
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
Agora, vamos definir nossa função geradora prime_numbers:
def prime_numbers(n):
for i in range(2, n+1):
if _is_prime(i):
yield i
Vamos testar nosso gerador:
for prime in prime_numbers(20):
print(prime)
Saída:
2
3
5
7
11
13
17
19
Expressão Geradora
Uma expressão geradora (generator expression) é semelhante a uma compreensão de lista (list comprehension), mas em vez de criar uma lista, ela retorna um objeto gerador.
Uma expressão geradora é definida usando parênteses (), e pode incluir uma ou mais cláusulas for. Ela é avaliada sob demanda e retorna um objeto gerador que pode ser usado para gerar os elementos da expressão sob demanda.
Aqui está um exemplo de uma expressão geradora que gera os quadrados de uma lista de números:
generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in generator:
print(x)
Saída:
1
4
9
16
25
Expressões geradoras são úteis porque nos permitem gerar elementos sob demanda, em vez de gerar todos os elementos antecipadamente. Essa pode ser uma abordagem mais eficiente, pois nos permite evitar a geração e o armazenamento de elementos desnecessários.
Expressões geradoras também são usadas para implementar avaliação preguiçosa (lazy evaluation) em Python. Isso significa que os elementos de uma expressão geradora são gerados apenas quando são necessários, em vez de gerar todos os elementos antecipadamente. Essa pode ser uma abordagem mais eficiente, pois nos permite evitar a geração e o armazenamento de elementos desnecessários.
Aqui estão alguns casos de uso comuns para expressões geradoras em Python:
- Gerar elementos sob demanda, em vez de gerar todos os elementos antecipadamente.
- Implementar avaliação preguiçosa de um grande conjunto de dados.
- Escrever código conciso e eficiente.
Expressões geradoras são uma ferramenta poderosa em Python e podem ser usadas para escrever código eficiente e elegante.
Expressão Geradora e List Comprehension
Aqui está um exemplo de uma compreensão de lista e uma expressão geradora que geram os quadrados de uma lista de números:
## Compreensão de lista
squares = [x**2 for x in [1, 2, 3, 4, 5]]
print(squares)
## Expressão geradora
squares_generator = (x**2 for x in [1, 2, 3, 4, 5])
for x in squares_generator:
print(x)
Saída:
[1, 4, 9, 16, 25]
1
4
9
16
25
Existem várias semelhanças e diferenças entre compreensões de lista e expressões geradoras:
Semelhanças
- Tanto as compreensões de lista quanto as expressões geradoras são usadas para gerar uma sequência de elementos.
- Ambas usam a mesma sintaxe, com uma ou mais cláusulas
fore uma expressão para gerar os elementos.
Diferenças
- Uma compreensão de lista gera uma lista, enquanto uma expressão geradora gera um objeto gerador.
- Uma compreensão de lista gera todos os elementos da lista antecipadamente, enquanto uma expressão geradora gera os elementos sob demanda.
- Uma compreensão de lista usa mais memória, pois armazena todos os elementos em uma lista, enquanto uma expressão geradora usa menos memória, pois gera os elementos sob demanda.
- Uma compreensão de lista geralmente é mais rápida de executar, pois gera todos os elementos antecipadamente, enquanto uma expressão geradora geralmente é mais lenta, pois gera os elementos sob demanda.
No geral, tanto as compreensões de lista quanto as expressões geradoras são ferramentas úteis para gerar uma sequência de elementos em Python. Compreensões de lista são geralmente mais rápidas e usam mais memória, enquanto expressões geradoras são geralmente mais lentas e usam menos memória. Qual usar depende dos requisitos específicos da aplicação.
Resumo
Neste laboratório, aprendemos sobre Iteradores, Geradores e Expressões Geradoras (Generator Expressions) embutidos no Python. Vimos como essas construções podem ser usadas para escrever código eficiente e elegante em Python. Também vimos um exemplo de como usar geradores para implementar um gerador de números primos.



