3.0 KiB
Generator
Introduction
Un generator est :
- Une fonction retournant un iterable,
- Un iterable est un object implémentant les méthodes
__iter__
et__next__
et génère une erreurStopIteration
lorsqu'il n'y a plus de valeur à retourner, - Utilise le statement
yield
au lieu dereturn
.
Le statement return
termine complètement une fonction alors que yield
la suspend (sauvegarde de son contexte afin
de continuer là où elle en était lors de son prochain appel).
Différences entre une function et un generator :
- Présence d'au moins un
yield
dans ungenerator
, - Le generator retourne un iterator (les méthodes
__iter__
et__next__
sont implémentées automatiquement), - Sauvegarde du contexte d'un generator,
- L'erreur
StopIteration
est générée automatiquement lors qu'un generator termine.
Les generator sont appropriés quand il est nécessaire de produire un flux de données infini.
def simple_generator():
for num in range(1, 4):
print(num)
yield num
gen = simple_generator()
next(gen)
next(gen)
next(gen)
try:
next(gen)
except StopIteration as __:
print('StopIteration raised')
for num in simple_generator():
pass
1 2 3 StopIteration raised 1 2 3
Un generator peut être créé soit en déclarant une fonction (cf. exemple précédent) ou directement via une generator expression :
list_numbers = [10, 18, 13, 23]
list_square = [x**2 for x in list_numbers]
generator = (x**2 for x in list_numbers)
print(list_square)
print(generator)
[100, 324, 169, 529] <generator object <genexpr> at 0x7fae7b90bb30>
Différences avec list
La principale différence entre les list comprehension et les generator expression consiste dans le fait que l'ensemble de la list est créée par la list compréhension alors que seul un generator est produit par la generator expression. Cela apporte quelques avantages :
- Chaque résultat du generator n'est produit que suite à l'appel de
__next__
(lazy execution), - L'espace mémoire nécessaire s'en retrouve réduit,
Il peut s'avérer plus efficace d'utiliser un generator lorsque l'ensemble des résultats ne sera pas utilisé à l'inverse pour les list comprehension.
Enchainement de generator
Il est possible d'enchainer plusieurs generator :
def fibonacci_numbers(numbers):
x, y = 0, 1
for _ in range(numbers):
x, y = y, x + y
yield x
def square(numbers):
for num in numbers:
yield num**2
print(f'{sum(square(fibonacci_numbers(30))) = }')
sum(square(fibonacci_numbers(30))) = 1120149658760