English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Neste artigo, você aprenderá como usar geradores de Python para criar iterações de forma fácil, como eles diferem dos iteradores e das funções comuns, e por que usar eles.
Usando PythonConstruirIteradorHá muitos custos; devemos implementar uma classe usando os métodos __iter__() e __next__(), rastrear o estado interno, levantar StopIteration quando não há valores a serem retornados, etc.
Isso é tanto longo quanto contra-intuitivo. Os geradores podem ser úteis neste caso.
Os geradores de Python são uma maneira simples de criar iteradores. Todas as despesas mencionadas acima são automaticamente tratadas pelos geradores do Python.
Em resumo, um gerador é uma função que retorna um objeto (iterador) que podemos iterar (um valor de cada vez).
Criar um gerador em Python é muito simples. Assim como definir uma função comum usando a sentença yield em vez da sentença return.
Se uma função contiver pelo menos uma sentença yield (ela pode conter outras sentenças yield ou return), então ela se torna uma função geradora. yield e return retornam alguns valores da função.
A diferença está em que, quando a sentença return termina completamente uma função, a sentença yield pausa a função para salvar todo seu estado e continua a ser executada em chamadas posteriores.
Isso é a diferença entre o função geradora eFunções comunsDiferenças.
As funções geradoras contêm uma ou mais sentenças yield.
Ao ser chamado, ele retorna um objeto (iterador), mas não começa a executar imediatamente.
Métodos como __iter__() e __next__() são automaticamente implementados. Portanto, podemos usar next() para percorrer os itens.
Assim que a função gera um resultado, a função pára e o controle é transferido para o chamador.
Variáveis locais e seu estado são lembrados entre chamadas consecutivas.
Por fim, quando a função termina, StopIteration é automaticamente levantada em chamadas posteriores.
Este é um exemplo para ilustrar todos os pontos mencionados acima. Temos uma função geradora chamada my_gen() nomeada por várias sentenças yield.
# Uma função de geração simples def my_gen(): n = 1 print('Esta é a primeira impressão') # A função de geração contém a instrução yield yield n n += 1 print('Esta é a segunda impressão') yield n n += 1 print('Esta é a última impressão') yield n
A execução interativa no interpretador é como mostrado a seguir. Execute esses comandos no Python Shell para ver a saída.
>>> # Ele retorna um objeto, mas não começa a executar imediatamente. >>> a = my_gen() >>> # Podemos usar next() para percorrer esses itens. >>> next(a) Esta é a primeira impressão 1 >>> # Assim que a função gera um resultado, a função pára e o controle é transferido para o chamador. >>> # As variáveis locais e seu estado são lembrados entre chamadas contínuas. >>> next(a) Esta é a segunda impressão 2 >>> next(a) Esta é a última impressão 3 >>> # Quando a função termina, uma StopIteration é automaticamente acionada na próxima chamada. >>> next(a) Traceback (última chamada): ... StopIteration >>> next(a) Traceback (última chamada): ... StopIteration
Algo interessante a ser notado no exemplo acima é que as variáveis são lembradas entre chamadas,ndo valor.
Diferente das funções normais, as variáveis locais não são destruídas quando a função é gerada. Além disso, o objeto de geração pode ser iterado apenas uma vez.
Para reiniciar o processo, precisamos usar algo como = my_gen() para criar outro objeto de gerador.
Atenção:O último ponto a ser notado é que podemos usar geradores diretamente comloop forusados juntos.
Isso acontece porque o loop for aceita um iterador e o itera usando a função next(). Quando StopIteration é acionado, ele termina automaticamente.Entenda como implementar loops for em Python na prática.
# Uma função de geração simples def my_gen(): n = 1 print('Esta é a primeira impressão') # A função de geração contém a instrução yield yield n n += 1 print('Esta é a segunda impressão') yield n n += 1 print('Esta é a última impressão') yield n # Usando loop for for item in my_gen(): print(item)
Quando o programa é executado, a saída é:
Esta é a primeira impressão 1 Esta é a segunda impressão 2 Esta é a última impressão 3
O exemplo acima não é muito útil, estudamos-o apenas para entender o que acontece no fundo.
Normalmente, as funções de geração são implementadas por meio de loops com condições de terminação apropriadas.
Vamos usar um exemplo de gerador para inverter strings.
def rev_str(my_str): length = len(my_str) for i in range(length - 1,-1,-1): yield my_str[i] # O loop for é usado para inverter a string # Saída: # o # l # l # e # h for char in rev_str("hello"): print(char)
Neste exemplo, usamos a função range() para obter índices em ordem inversa usando um loop for.
A verdade é que essa função geradora não se aplica apenas a strings, mas também a outros tipos de objetos iteráveis, comolista,tuplaetc.
Usar expressões geradoras permite criar facilmente geradores simples de forma dinâmica. Ela facilita a construção de geradores.
criadas porfunções anônimascriando funções geradoras anônimas. Elas são iguais a
A sintaxe das expressões geradoras é semelhante aPythonemCompreensão de listaSintaxe. Mas substitua os colchetes por parênteses.
A principal diferença entre compreensão de lista e expressões geradoras está em que, enquanto a compreensão de lista gera toda a lista, a expressão geradora gera um item de cada vez.
Eles são um pouco preguiçosos, gerando itens apenas quando necessário. Por essa razão, as expressões geradoras são muito mais eficientes em termos de uso de memória do que compreensões de lista equivalentes.
# Inicialização da lista my_list = [1, 3, 6, 10] # Usar compreensão de lista para quadruplicar cada item # Saída: [1, 9, 36, 100] [x**2 para x em my_list] # A mesma coisa pode ser feita usando expressões geradoras # Saída: <generator object <genexpr> em 0x0000000002EBDAF8> (x**2 para x em my_list)
Podemos ver que a expressão geradora não gera o resultado necessário imediatamente. Em vez disso, ela retorna um objeto gerador que produz itens conforme necessário.
# Inicialização da lista my_list = [1, 3, 6, 10] a = (x**2 para x em my_list) # Saída: 1 print(next(a)) # Saída: 9 print(next(a)) # Saída: 36 print(next(a)) # Saída: 100 print(next(a)) # Saída: StopIteration next(a)
As expressões geradoras podem ser usadas dentro de funções. Quando usadas dessa maneira, podem ser removidos os parênteses.
>>> somar(x**2 para x em my_list) 146 >>> max(x**2 para x em my_list) 100
Existem várias razões que tornam os geradores uma implementação atraente.
Em comparação com os itens correspondentes de suas classes iteradoras, os geradores podem ser implementados de maneira clara e concisa. Abaixo está um exemplo de implementação usando a classe iterator2um exemplo de sequência de potências.
class PowTwo: def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 retornar self def __next__(self): se self.n > self.max: levantar StopIteration result = 2 ** self.n self.n += 1 return result
This code is very long. Now, perform the same operation using the generator function.
def PowTwoGen(max = 0): n = 0 while n < max: yield 2 ** n n += 1
Since generators automatically track details, they are concise and clear, and they are also easier to implement.
A regular function that returns a sequence creates the entire sequence in memory before returning the result. If the number of items in the sequence is large, it can affect efficiency.
And the generator implementation of this sequence is memory-friendly, so it is the preferred choice because it can only generate one item at a time.
Generators are an excellent medium for representing infinite data streams. Infinite streams cannot be stored in memory, and since generators generate one item at a time, they can represent infinite data streams.
The following example can generate all even numbers (at least in theory).
def all_even(): n = 0 while True: yield n n += 2
Generators can be used for pipelining a series of operations. It is best to illustrate with an example.
Assuming we have a log file of a famous fast-food chain. The log file has a column (the4Column), which tracks the number of pizzas sold per hour, we want to sum it up to get5Total number of pizzas sold in the year.
Assuming all content is strings and no available numbers are marked as "N / A. Generator implementation can be as follows.
with open('sells.log') as file: pizza_col = (line[3] for line in file) per_hour = (int(x) for x in pizza_col if x != 'N/A') print("Total pizzas sold = ", sum(per_hour))
This pipeline is efficient and easy to read (yes, very cool!).