<jupyter_start><jupyter_text>Les générateurs Les générateurs sont très simples à utiliser et très puissants. Ils vous permettront d'optimiser votre code à moindre frais. Alors pourquoi se priver ? Imaginons que je veuille extraire d'une liste de mots la liste des mots comportants le caractère 'a'. Je vais écrire une fonction.<jupyter_code>def with_a(words):
    """
    Reçoit une liste de mots et renvoie la liste des mots contenant le car. 'a'
    """
    res = []
    for word in words:
        if 'a' in word:
            res.append(word)
    return res
    
mots = ["le", "petit", "chat", "est", "mort", "ce", "matin"]
mots_a = with_a(mots)
print("\n".join(mots_a))<jupyter_output><empty_output><jupyter_text>Jusque là rien de méchant. Comme il est question d'optimisation je vais mesurer le temps de traitement avec `timeit`.  ipython est plein de magie, `%time` hup hup hup barbatruc et voilà.<jupyter_code>%time mots_a = with_a(mots)
mots_big = mots * 1000000
%time mots_a = with_a(mots_big)<jupyter_output><empty_output><jupyter_text>Comme on pouvait s'y attendre le temps d'exécution de la fonction augmente avec la taille de la liste initiale.  Voyons ce que ça donne avec un générateur. Construire un générateur c'est simple : vous remplacez `return` par `yield` dans votre fonction.  C'est tout ? C'est tout.  Vous pouvez quand même en apprendre plus [ici](http://intermediatepythonista.com/python-generators) ou lire la [PEP 255](https://www.python.org/dev/peps/pep-0255/) si vous aimez ça.<jupyter_code>def gen_with_a(words):
    """
    Reçoit une liste de mots et renvoie les mots contenant le car. 'a' sous forme de générateur
    """
    for word in words:
        if 'a' in word:
            yield(word)
mots_big = mots * 100
%time mots_a = with_a(mots_big)
%time mots_a_gen = gen_with_a(mots_big)<jupyter_output><empty_output><jupyter_text>😲 !!!!!!!!!  Oui c'est de la magie. Enfin c'est plutôt de la triche, regardez :<jupyter_code>print("mots_a is a {}".format(type(mots_a)))
print("mots_a_gen is a {}".format(type(mots_a_gen)))
import sys
print("Taille de mots_a : {}".format(sys.getsizeof(mots_a)))
print("Taille de mots_a_gen : {}".format(sys.getsizeof(mots_a_gen)))<jupyter_output><empty_output><jupyter_text>`mots_a_gen` n'est pas une liste, c'est un objet `generator`.  Il ne stocke rien ou presque en mémoire, on ne peut pas connaître sa taille (essayez `len(mots_a_gen)` pour voir.  Mais c'est un itérable, on peut le parcourir comme une liste. Par contre on ne peut pas les "trancher", on ne peut accéder à un élément d'index `i` comme pour une liste.  Encore une différence d'avec les listes : vous ne pouvez parcourir un générateur qu'une seule fois.<jupyter_code>%time mots_a_gen = list(gen_with_a(mots_big))<jupyter_output><empty_output><jupyter_text>Mais même sans tricher les générateurs demeurent très efficaces. Vous aurez compris qu'il vous est désormais chaudement recommandé de les utiliser.  Vous pouvez aussi utiliser des générateurs en compréhension, à la manière des listes en compréhension :<jupyter_code>[mot for mot in mots if 'a' in mot]
(mot for mot in mots if 'a' in mot)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09949302915082382, 'fraction_numerical': 0.005386565272496832, 'mean_word_length': 4.824723247232472, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '212643', 'license': 'MIT', 'n_tokens_mistral': 1193, 'n_tokens_neox': 1168, 'n_words': 379, 'path': 'generateur.ipynb', 'repo_name': 'LoicGrobol/python-im', 'size': '3156'}