Función generadora
Una "función" que devuelve datos con una declaración de yield
, denominada función de generador. Reescribimos la clase personalizada LessThan
en la sección anterior en una función de generador:
In [30]: def lessthan(n):
...: for i in range(n-1, -1, -1):
...: yield i
...:
...:
In [31]: for i in lessthan(5):
...: print(i)
...:
4
3
2
1
0
In [32]: lt = lessthan(3)
## 查看生成器对象的__iter__()和__next__():
In [33]: lt.__iter__?
Signature: lt.__iter__()
Call signature: lt.__iter__(*args, **kwargs)
Type: method-wrapper
String form: <method-wrapper '__iter__' of generator object at 0x7fc048cb8ba0>
Docstring: Implement iter(self).
In [34]: lt.__next__?
Signature: lt.__next__()
Call signature: lt.__next__(*args, **kwargs)
Type: method-wrapper
String form: <method-wrapper '__next__' of generator object at 0x7fc048cb8ba0>
Docstring: Implement next(self).
Después de reescribir la clase LessThan
con el generador, el código es más compacto y compacto, porque crea automáticamente los __iter__()
y __next__()
, y el bucle for
puede atravesar el objeto generador.
A continuación, definimos un objeto generador lt
, invocamos next()
en este objeto generador, y cada vez que se invoca, reanudará la ejecución desde la última vez que lo dejó (es decir, recuerde todos los valores de datos cuando se ejecutó la instrucción por última vez). Se StopIteration
error StopIteration
cuando el generador genera todos los elementos (el generador termina).
In [53]: lt = lessthan(3)
In [54]: next(lt)
Out[54]: 2
In [55]: next(lt)
Out[55]: 1
In [56]: next(lt)
Out[56]: 0
In [57]: next(lt)
---------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-37-00f31299a3f9> in <module>
----> 1 next(lt)
StopIteration:
Resolución del generador
Para implementar algunos generadores simples, podemos usar la forma de una sintaxis similar a un análisis en lugar de una función, y reemplazar los corchetes externos con paréntesis.
Las expresiones de los generadores son más compactas pero menos flexibles que los generadores completos, y son más eficientes en memoria que las comprensiones de listas equivalentes. Por ejemplo, en el siguiente código, cada elemento de mylist
generado por la expresión de la lista se almacena en la memoria, y mygener
generará un elemento en cada iteración. Suponiendo que el número de elementos no sea 10, sino 1 millón o más, la ventaja de memoria del generador será muy obvia.
In [41]: mylist = [i*i for i in range(10)]
In [42]: mylist
Out[42]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In [43]: mygener = (i*i for i in range(10))
In [44]: mygener
Out[44]: <generator object <genexpr> at 0x7fc048be3bf8>
El análisis del generador está diseñado para situaciones en las que el generador será utilizado inmediatamente por la función externa, como por ejemplo:
In [45]: sum(i*i for i in range(10))
Out[45]: 285
El i*i for i in range(10)
corchete sum()
es un analizador de generador que evita generar una lista y ocupar demasiada memoria.
Del mismo modo, los siguientes ejemplos utilizan el análisis de generador:
xvec = [10, 20, 30]
yvec = [7, 5, 3]
sum(x*y for x,y in zip(xvec, yvec)) # dot product
from math import pi, sin
sine_table = {x: sin(x*pi/180) for x in range(0, 91)}
unique_words = set(word for line in page for word in line.split())
valedictorian = max((student.gpa, student.name) for student in graduates)
data = 'golf'
list(data[i] for i in range(len(data)-1, -1, -1))
Resumen
Python proporciona dos formas de implementar generadores:
(1) La función del generador <br /> es sintácticamente similar a la función ordinaria, reemplazando el valor de return
con yield
lugar del yield
; implementando automáticamente el protocolo del iterador: __iter__()
y método __next__()
. Cuando no hay ningún valor para devolver, se produce una excepción StopInteration
. yield
declaración de yield
suspende el estado de la función del generador para que continúe desde el estado de salida cuando se repite la iteración.
(2) Análisis del generador <br /> Similar al análisis de la lista, reemplazando los corchetes con paréntesis, por lo tanto simplemente implementando un generador simple.
(3) Ventajas del generador <br /> El código es compacto y ahorra memoria. A diferencia de una lista que se puede recorrer varias veces, el generador solo puede recorrerla una vez.
留言
張貼留言