Weekly backup.
This commit is contained in:
98
20220529123824-generator.org
Normal file
98
20220529123824-generator.org
Normal file
@@ -0,0 +1,98 @@
|
||||
:PROPERTIES:
|
||||
:ID: 67410dad-d959-4029-b281-9bf1c9e69ede
|
||||
:mtime: 20220529131239
|
||||
: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
|
||||
|
||||
* 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]]
|
Reference in New Issue
Block a user