Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> python

Generadores de Python

Generadores Python

En este tutorial, aprenderá cómo crear iteraciones fácilmente usando generadores de Python, en qué se diferencia de los iteradores y las funciones normales, y por qué debería usarlo.

Video:Generadores Python

Generadores en Python

Hay mucho trabajo en la construcción de un iterador en Python. Tenemos que implementar una clase con __iter__() y __next__() método, realizar un seguimiento de los estados internos y generar StopIteration cuando no hay valores para devolver.

Esto es largo y contraintuitivo. Generator viene al rescate en tales situaciones.

Los generadores de Python son una forma sencilla de crear iteradores. Todo el trabajo que mencionamos anteriormente es manejado automáticamente por generadores en Python.

En pocas palabras, un generador es una función que devuelve un objeto (iterador) sobre el que podemos iterar (un valor a la vez).


Crear generadores en Python

Es bastante simple crear un generador en Python. Es tan fácil como definir una función normal, pero con un yield declaración en lugar de un return declaración.

Si una función contiene al menos un yield declaración (puede contener otros yield o return sentencias), se convierte en una función generadora. Ambos yield y return devolverá algún valor de una función.

La diferencia es que mientras un return declaración termina una función por completo, yield La declaración detiene la función guardando todos sus estados y luego continúa desde allí en llamadas sucesivas.


Diferencias entre la función Generador y la función Normal

Así es como una función de generador difiere de una función normal.

  • La función de generador contiene uno o más yield declaraciones.
  • Cuando se llama, devuelve un objeto (iterador) pero no inicia la ejecución inmediatamente.
  • Métodos como __iter__() y __next__() se implementan automáticamente. Entonces podemos iterar a través de los elementos usando next() .
  • Una vez que la función cede, la función se pausa y el control se transfiere a la persona que llama.
  • Las variables locales y sus estados se recuerdan entre llamadas sucesivas.
  • Finalmente, cuando la función termina, StopIteration se genera automáticamente en llamadas posteriores.

Aquí hay un ejemplo para ilustrar todos los puntos mencionados anteriormente. Tenemos una función generadora llamada my_gen() con varios yield declaraciones.

# 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

A continuación se muestra una ejecución interactiva en el intérprete. Ejecútelos en el shell de Python para ver el resultado.

>>> # 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

Una cosa interesante a tener en cuenta en el ejemplo anterior es que el valor de la variable n se recuerda entre cada llamada.

A diferencia de las funciones normales, las variables locales no se destruyen cuando la función cede. Además, el objeto generador se puede iterar solo una vez.

Para reiniciar el proceso necesitamos crear otro objeto generador usando algo como a = my_gen() .

Una última cosa a tener en cuenta es que podemos usar generadores con bucles for directamente.

Esto se debe a que un for loop toma un iterador y lo itera usando next() función. Termina automáticamente cuando StopIteration es elevado. Consulte aquí para saber cómo se implementa realmente un bucle for en 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)

Cuando ejecute el programa, la salida será:

This is printed first
1
This is printed second
2
This is printed at last
3

Generadores de Python con bucle

El ejemplo anterior es de menor utilidad y lo estudiamos solo para tener una idea de lo que estaba sucediendo en segundo plano.

Normalmente, las funciones del generador se implementan con un bucle que tiene una condición de terminación adecuada.

Tomemos un ejemplo de un generador que invierte una cadena.

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)

Salida

o
l
l
e
h

En este ejemplo, hemos utilizado el range() función para obtener el índice en orden inverso usando el ciclo for.

Nota :Esta función generadora no solo funciona con cadenas, sino también con otros tipos de iterables como lista, tupla, etc.


Expresión del generador de Python

Los generadores simples se pueden crear fácilmente sobre la marcha usando expresiones de generador. Facilita la construcción de generadores.

Al igual que las funciones lambda que crean funciones anónimas, las expresiones generadoras crean funciones generadoras anónimas.

La sintaxis para la expresión del generador es similar a la de una lista de comprensión en Python. Pero los corchetes se reemplazan con paréntesis redondos.

La principal diferencia entre una lista por comprensión y una expresión generadora es que una lista por comprensión produce la lista completa, mientras que la expresión generadora produce un elemento a la vez.

Tienen una ejecución perezosa (producen elementos solo cuando se les solicita). Por esta razón, una expresión generadora es mucho más eficiente en términos de memoria que una comprensión 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)

Salida

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

Podemos ver arriba que la expresión del generador no produjo el resultado requerido inmediatamente. En su lugar, devolvió un objeto generador, que produce artículos solo bajo demanda.

Así es como podemos comenzar a obtener elementos del generador:

# 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)

Cuando ejecutamos el programa anterior, obtenemos el siguiente resultado:

1
9
36
100
Traceback (most recent call last):
  File "<string>", line 15, in <module>
StopIteration

Las expresiones generadoras se pueden utilizar como argumentos de función. Cuando se usa de esa manera, los paréntesis redondos se pueden eliminar.

>>> sum(x**2 for x in my_list)
146

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

Uso de generadores Python

Hay varias razones que hacen que los generadores sean una implementación poderosa.

1. Fácil de implementar

Los generadores se pueden implementar de una manera clara y concisa en comparación con su contraparte de la clase iterador. El siguiente es un ejemplo para implementar una secuencia de potencia de 2 usando una clase de iterador.

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

El programa anterior fue largo y confuso. Ahora, hagamos lo mismo usando una función de generador.

def PowTwoGen(max=0):
    n = 0
    while n < max:
        yield 2 ** n
        n += 1

Dado que los generadores realizan un seguimiento de los detalles automáticamente, la implementación fue concisa y mucho más limpia.

2. Memoria Eficiente

Una función normal para devolver una secuencia creará la secuencia completa en la memoria antes de devolver el resultado. Esto es una exageración, si la cantidad de elementos en la secuencia es muy grande.

La implementación del generador de tales secuencias es amigable con la memoria y se prefiere ya que solo produce un elemento a la vez.

3. Representar flujo infinito

Los generadores son medios excelentes para representar un flujo infinito de datos. Los flujos infinitos no se pueden almacenar en la memoria y, dado que los generadores producen solo un elemento a la vez, pueden representar un flujo infinito de datos.

La siguiente función generadora puede generar todos los números pares (al menos en teoría).

def all_even():
    n = 0
    while True:
        yield n
        n += 2

4. Generadores de tuberías

Se pueden usar múltiples generadores para canalizar una serie de operaciones. Esto se ilustra mejor con un ejemplo.

Supongamos que tenemos un generador que produce los números en la serie de Fibonacci. Y tenemos otro generador para elevar números al cuadrado.

Si queremos encontrar la suma de los cuadrados de los números en la serie de Fibonacci, podemos hacerlo de la siguiente manera canalizando la salida de las funciones del generador juntas.

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))))

Salida

4895

Esta canalización es eficiente y fácil de leer (¡y sí, mucho más genial!).


python

  1. Operadores de Python
  2. Argumentos de la función de Python
  3. Diccionario de Python
  4. Generadores de Python
  5. Cierres Python
  6. Decoradores de pitón
  7. Funciones Python Lambda con EJEMPLOS
  8. Función Python abs():Ejemplos de valores absolutos
  9. Función Python round() con EJEMPLOS
  10. Función Python map() con EJEMPLOS
  11. Tutorial de Rendimiento en Python:Generador y Rendimiento vs Ejemplo de Retorno