: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] : 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]]