Files
org-roamings/20220529123824-generator.org

139 lines
3.8 KiB
Org Mode

:PROPERTIES:
:ID: 67410dad-d959-4029-b281-9bf1c9e69ede
:mtime: 20220729213822
:ctime: 20220529123824
:END:
#+title: 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 erreur ~StopIteration~ lorsqu'il n'y a plus de valeur à retourner,
* Utilise le /statement/ ~yield~ au lieu de ~return~.
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 un ~generator~,
* 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.
#+BEGIN_SRC python :results output
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
#+END_SRC
#+RESULTS:
: 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/ :
#+BEGIN_SRC python :results output
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)
#+END_SRC
#+RESULTS:
: [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/ :
#+BEGIN_SRC python :results output
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))) = }')
#+END_SRC
#+RESULTS:
: sum(square(fibonacci_numbers(30))) = 1120149658760
* Renvoyer une valeur à un /generator/
#+BEGIN_SRC python :results output
def dummy_generator():
i = 0
for i in range(10):
print(f'from generator {yield i}')
generator = dummy_generator()
received = generator.send(None)
print(f'from here {received}')
try:
while True:
received = generator.send(received + 100)
print(f'from here {received}')
except StopIteration:
pass
#+END_SRC
#+RESULTS:
: from here 0
: from generator 100
: from here 1
: from generator 101
: from here 2
: from generator 102
: from here 3
: from generator 103
: from here 4
: from generator 104
: from here 5
: from generator 105
: from here 6
: from generator 106
: from here 7
: from generator 107
: from here 8
: from generator 108
: from here 9
: from generator 109
* Références
* [[https://blog.devgenius.io/what-is-generator-in-python-and-how-does-it-work-e6e0588785c3][What Is Generator in Python and How Does It Work ? - Medium]]