Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial programming >> python

Geradores Python

Geradores Python


Neste tutorial, você aprenderá como criar iterações facilmente usando geradores Python, como ele é diferente de iteradores e funções normais e por que você deve usá-lo.

Vídeo:Geradores Python

Geradores em Python


Há muito trabalho na construção de um iterador em Python. Temos que implementar uma classe com __iter__() e __next__() método, acompanhe os estados internos e aumente StopIteration quando não há valores a serem retornados.

Isso é longo e contra-intuitivo. Generator vem em socorro em tais situações.

Geradores Python são uma maneira simples de criar iteradores. Todo o trabalho que mencionamos acima é tratado automaticamente por geradores em Python.

Simplesmente falando, um gerador é uma função que retorna um objeto (iterador) sobre o qual podemos iterar (um valor de cada vez).

Criar geradores em Python


É bastante simples criar um gerador em Python. É tão fácil quanto definir uma função normal, mas com um yield instrução em vez de um return declaração.

Se uma função contém pelo menos um yield declaração (pode conter outros yield ou return instruções), torna-se uma função geradora. Ambos yield e return retornará algum valor de uma função.

A diferença é que enquanto um return termina uma função inteiramente, yield A instrução pausa a função salvando todos os seus estados e depois continua a partir daí em sucessivas chamadas.

Diferenças entre a função Gerador e a função Normal


Aqui está como uma função geradora difere de uma função normal.

Aqui está um exemplo para ilustrar todos os pontos mencionados acima. Temos uma função geradora chamada my_gen() com vários yield declarações.
# A simple generator function
def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n

Uma execução interativa no interpretador é fornecida abaixo. Execute-os no shell do Python para ver a saída.
>>> # It returns an object but does not start execution immediately.
>>> a = my_gen()

>>> # We can iterate through the items using next().
>>> next(a)
This is printed first
1
>>> # Once the function yields, the function is paused and the control is transferred to the caller.

>>> # Local variables and theirs states are remembered between successive calls.
>>> next(a)
This is printed second
2

>>> next(a)
This is printed at last
3

>>> # Finally, when the function terminates, StopIteration is raised automatically on further calls.
>>> next(a)
Traceback (most recent call last):
...
StopIteration
>>> next(a)
Traceback (most recent call last):
...
StopIteration

Uma coisa interessante a ser observada no exemplo acima é que o valor da variável n é lembrado entre cada chamada.

Ao contrário das funções normais, as variáveis ​​locais não são destruídas quando a função rende. Além disso, o objeto gerador pode ser iterado apenas uma vez.

Para reiniciar o processo, precisamos criar outro objeto gerador usando algo como a = my_gen() .

Uma última coisa a notar é que podemos usar geradores com loops for diretamente.

Isso ocorre porque um for loop pega um iterador e itera sobre ele usando next() função. Ele termina automaticamente quando StopIteration é levantada. Confira aqui para saber como um loop for é realmente implementado em Python.
# A simple generator function
def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n


# Using for loop
for item in my_gen():
    print(item)

Ao executar o programa, a saída será:
This is printed first
1
This is printed second
2
This is printed at last
3

Geradores Python com um Loop


O exemplo acima é menos útil e nós o estudamos apenas para ter uma ideia do que estava acontecendo em segundo plano.



Normalmente, as funções do gerador são implementadas com um loop com uma condição de terminação adequada.

Vamos dar um exemplo de um gerador que inverte uma string.
def rev_str(my_str):
    length = len(my_str)
    for i in range(length - 1, -1, -1):
        yield my_str[i]


# For loop to reverse the string
for char in rev_str("hello"):
    print(char)

Saída
o
l
l
e
h

Neste exemplo, usamos o range() função para obter o índice na ordem inversa usando o loop for.

Observação :Esta função geradora não só funciona com strings, mas também com outros tipos de iteráveis ​​como lista, tupla, etc.

Expressão do Gerador Python


Geradores simples podem ser facilmente criados em tempo real usando expressões de gerador. Facilita a construção de geradores.

Semelhante às funções lambda que criam funções anônimas, expressões geradoras criam funções geradoras anônimas.

A sintaxe para expressão do gerador é semelhante à de uma compreensão de lista em Python. Mas os colchetes são substituídos por parênteses redondos.

A principal diferença entre uma compreensão de lista e uma expressão geradora é que uma compreensão de lista produz a lista inteira enquanto a expressão geradora produz um item por vez.

Eles têm execução preguiçosa (produzindo itens apenas quando solicitados). Por esse motivo, uma expressão geradora é muito mais eficiente em termos de memória do que uma compreensão de lista equivalente.
# Initialize the list
my_list = [1, 3, 6, 10]

# square each term using list comprehension
list_ = [x**2 for x in my_list]

# same thing can be done using a generator expression
# generator expressions are surrounded by parenthesis ()
generator = (x**2 for x in my_list)

print(list_)
print(generator)

Saída
[1, 9, 36, 100]
<generator object <genexpr> at 0x7f5d4eb4bf50>

Podemos ver acima que a expressão geradora não produziu o resultado desejado imediatamente. Em vez disso, ele retornou um objeto gerador, que produz itens apenas sob demanda.

Aqui está como podemos começar a obter itens do gerador:
# Initialize the list
my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)
print(next(a))

print(next(a))

print(next(a))

print(next(a))

next(a)

Quando executamos o programa acima, obtemos a seguinte saída:
1
9
36
100
Traceback (most recent call last):
  File "<string>", line 15, in <module>
StopIteration

As expressões do gerador podem ser usadas como argumentos de função. Quando usado dessa maneira, os parênteses redondos podem ser eliminados.
>>> sum(x**2 for x in my_list)
146

>>> max(x**2 for x in my_list)
100

Uso de geradores Python


Existem várias razões que tornam os geradores uma implementação poderosa.

1. Fácil de implementar


Os geradores podem ser implementados de maneira clara e concisa em comparação com sua contraparte da classe iteradora. A seguir está um exemplo para implementar uma sequência de potência de 2 usando uma classe iteradora.
class PowTwo:
    def __init__(self, max=0):
        self.n = 0
        self.max = max

    def __iter__(self):
        return self

    def __next__(self):
        if self.n > self.max:
            raise StopIteration

        result = 2 ** self.n
        self.n += 1
        return result

O programa acima era longo e confuso. Agora, vamos fazer o mesmo usando uma função de gerador.
def PowTwoGen(max=0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

Como os geradores acompanham os detalhes automaticamente, a implementação foi concisa e muito mais limpa.

2. Memória eficiente


Uma função normal para retornar uma sequência criará a sequência inteira na memória antes de retornar o resultado. Isso é um exagero, se o número de itens na sequência for muito grande.

A implementação do gerador de tais sequências é amigável à memória e é preferida, pois produz apenas um item por vez.

3. Representar Fluxo Infinito


Os geradores são excelentes meios para representar um fluxo infinito de dados. Fluxos infinitos não podem ser armazenados na memória e, como os geradores produzem apenas um item por vez, eles podem representar um fluxo infinito de dados.

A seguinte função geradora pode gerar todos os números pares (pelo menos em teoria).
def all_even():
    n = 0
    while True:
        yield n
        n += 2

4. Geradores de pipeline


Vários geradores podem ser usados ​​para canalizar uma série de operações. Isso é melhor ilustrado usando um exemplo.

Suponha que tenhamos um gerador que produz os números da série de Fibonacci. E temos outro gerador para elevar os números ao quadrado.

Se quisermos descobrir a soma dos quadrados dos números na série de Fibonacci, podemos fazê-lo da seguinte maneira, canalizando a saída das funções geradoras.
def fibonacci_numbers(nums):
    x, y = 0, 1
    for _ in range(nums):
        x, y = y, x+y
        yield x

def square(nums):
    for num in nums:
        yield num**2

print(sum(square(fibonacci_numbers(10))))

Saída
4895

Este pipelining é eficiente e fácil de ler (e sim, muito mais legal!).

python

  1. Operadores Python
  2. Argumentos da função Python
  3. Dicionário Python
  4. Geradores Python
  5. Fechamentos Python
  6. Decoradores Python
  7. Funções do Python Lambda com EXEMPLOS
  8. Função Python abs():exemplos de valor absoluto
  9. Função Python round() com EXEMPLOS
  10. Função Python map() com EXEMPLOS