Jose : Bonjour et bienvenue,
dans cette vidéo nous parlons des générateurs en Python.
Les générateurs sont
l'un de ces sujets en Python que beaucoup
de cours ne mentionnent même pas, mais
ils sont essentiels pour la
performance et aussi parce que le développement e-sync
en Python, le développement e-synchrone que nous allons apprendre plus tard,
est construit sur ces générateurs.
Lorsque vous comprenez les générateurs,
beaucoup de choses en Python deviennent beaucoup plus claires.
Qu'est-ce qu'un générateur ?
En Python, un générateur est une fonction.
Il s'agit d'une fonction spéciale.
Voir une fonction qui se souvient de l'état dans lequel elle
se trouve entre deux exécutions,
de sorte que vous pouvez exécuter la fonction plusieurs
fois et qu'elle se souviendra de ce qu'elle a fait
la dernière fois que vous l'avez exécutée.
Cela peut sembler bizarre, mais je vais vous expliquer avec un exemple.
Imaginez que vous souhaitiez créer une
liste de 100 numéros, c'est
très simple, vous voulez obtenir une nouvelle liste avec des numéros
de zéro à 99, sans inclure 100.
Vous pouvez donc créer une fonction comme celle-ci.
Il est évident que l'on n'utilise pas la gamme et ainsi de suite, vous connaissez la gamme.
N'utilisons pas la gamme, mais construisons une liste à partir de zéro.
Comment procéder ?
Une bonne façon de procéder serait de commencer par une liste vide.
Appelons-le nums et faisons en sorte qu'il soit égal à une liste vide.
Et commence également avec un indice égal à zéro.
Ensuite, tant que i est inférieur
à 100, say nums dot append i.
Vous allez donc ajouter zéro au début.
Ensuite, vous allez incrémenter i par un, ce qui
va le transformer en un.
Vous allez ajouter un, répéter, ajouter deux,
puis trois et ainsi de suite, jusqu'à ce que vous arriviez à 99.
À la fin de cette fonction, renvoyez
nums, qui est votre liste de nombres.
Si nous imprimons des centaines de chiffres et que nous les exécutons.
On constate qu'il contient
ici notre liste de cent numéros.
Bien sûr, nous pourrions utiliser la compréhension de liste pour cela.
Nous pourrions utiliser la fonction
range, mais pour l'instant, supposons qu'il
s'agit d'une bonne façon de procéder, car ce que j'explique en réalité,
c'est essentiellement la façon dont la fonction range fonctionne à l'intérieur.
Nous construisons donc une liste,
nous la remplissons avec les cent premiers chiffres, puis nous retournons.
Nous avons maintenant une liste de cent nombres.
Cette liste complète est stockée dans la mémoire de votre ordinateur sous la
forme de la valeur de retour de la fonction.
Maintenant, cent chiffres, ce n'est pas
très grand, c'est une petite quantité
d'espace, mais disons que nous voulons stocker 10 millions, un million
c'est comme cent chiffres, mais ce serait
quelque chose comme un million de chiffres.
Vous générez une liste d'un million de nombres et la
stockez dans cette variable
qui va dans la mémoire de votre ordinateur et
qui occupe vraisemblablement une plus grande quantité
de mémoire, parce que chacun de ces nombres
occupe une petite quantité de la mémoire de votre ordinateur.
Si vous voulez 10 millions de chiffres, c'est plus important.
Le fait d'avoir 10 millions de chiffres n'est pas un très bon exemple, mais
imaginez que vous ayez une très longue liste de quelque chose.
Par exemple, ils sont souvent utilisés pour
calculer les nombres premiers et dans le domaine
des données volumineuses (big data),
où l'on a parfois besoin de grandes listes d'éléments.
Un générateur est donc utilisé pour contourner ce problème,
car l'hypothèse est que vous n'avez pas besoin de 100 nombres
en une seule fois.
Ce dont vous avez besoin, c'est d'un zéro pour pouvoir en faire quelque chose.
Et un autre pour que vous puissiez en faire quelque chose.
Et ensuite deux pour que vous puissiez en faire quelque chose.
Par exemple, avec cette centaine de nombres,
nous pourrions les multiplier par deux et les stocker dans une autre liste.
Comment feriez-vous ?
Vous feriez quelque chose comme moins de compréhension,
x fois deux pour x en centaines de chiffres.
Ce que vous faites ici, c'est que vous utilisez
cette liste pour l'isoler et multiplier chaque nombre par deux.
Imaginons que cette multiplication par
deux soit une opération que nous voulons effectuer
sur ces nombres afin de calculer quelque chose.
Autre exemple, si vous avez beaucoup d'utilisateurs
et que vous voulez calculer l'engagement de ces derniers, ou leur
degré d'engagement dans votre système, ou
quelque chose de ce genre.
Il s'agit essentiellement de passer en revue
chaque élément et d'effectuer une opération.
Le fait est que vous n'avez pas besoin de tous les éléments en même temps.
Vous en avez besoin un par un.
C'est donc à cela que servent les générateurs.
Au lieu d'avoir la liste complète des éléments,
le générateur vous donne le premier
élément de la liste, sans stocker
toute la liste en mémoire.
La prochaine fois que vous appellerez
le générateur, il se souviendra de l'élément qu'il vous a donné la
dernière fois et il saura vous donner le deuxième élément.
Ensuite, vous l'exécutez à nouveau et il vous donne le troisième
élément, et ainsi de suite.
Il ne stocke jamais la liste en mémoire,
il ne se souvient que du dernier numéro qu'il vous a donné,
afin de pouvoir vous donner le suivant.
Vous devez activer la fonction chaque
fois que vous voulez un nouveau nombre, et c'est pourquoi
on l'appelle un générateur, parce qu'il génère des nombres,
ou des chaînes de caractères, ou des utilisateurs, ou tout ce que vous voulez générer.
Voici comment procéder, au lieu de "nums. append(i)", vous dites "yield
i", car i est le nombre que nous voulons retourner, nous
n'avons plus besoin de ce crochet
égal à nums, et nous n'avons plus besoin
du segment return non plus.
Cette fonction est donc appelée fonction génératrice.
Yield ressemble beaucoup à return, mais il vous
donne i lorsque vous l'appelez, puis il se souvient
qu'il est ici, il s'est arrêté juste avant
la ligne cinq. Juste après la ligne
4, au milieu.
La prochaine fois que vous appellerez la fonction, elle
continuera, donc ce qu'elle fera, c'est qu'elle
incrémentera i de un, elle réexécutera la boucle et produira
un, vous donnera un et s'arrêtera.
La prochaine fois que vous l'appellerez,
il continuera à incrémenter "j'en
achète un" et répétera la boucle pour vous en donner deux, puis il s'arrêtera, et ainsi de suite.
En fin de compte, lorsque vous serez à court
de chiffres, vous n'en obtiendrez aucun.
C'est alors que l'on sait que c'est terminé.
Alors, comment allons-nous faire ?
Tout d'abord, imprimons cent chiffres.
Remarquez qu'il ne s'agit plus d'une
liste, mais d'un objet générateur, qui
s'appelle cent nombres, d'où le nom
de notre fonction.
Il nous donne également l'adresse mémoire de notre objet.
Remarquez que nous avons défini une fonction, mais
que l'impression nous indique qu'il s'agit d'un objet, parce
que Python, en arrière-plan, se rend
compte que vous avez utilisé un rendement, que vous
créez un générateur, et qu'il le transforme en objet Python.
Je vous expliquerai plus loin comment
fabriquer vous-même ces objets sans fonction.
Stockons donc notre générateur dans une variable.
Nous allons l'appeler "g", c'est un nom assez
populaire pour un générateur dans un petit programme.
Si vous avez de nombreux générateurs,
vous devrez bien sûr opter pour un nom plus descriptif.
Nous allons ensuite imprimer la valeur suivante
que le générateur nous donnerait.
Remarquez que lorsque vous initialisez un générateur, comme
cela, il n'exécute pas la fonction, il commence tout en haut
sans s'exécuter du tout.
Lorsque vous lui demandez la valeur suivante, il s'exécute
jusqu'au rendement et vous donne
la valeur que vous avez cédée.
La façon dont vous lui demandez la valeur suivante,
c'est que vous n'exécutez pas la fonction à nouveau, mais vous dites la valeur suivante de "g".
La fonction suivante est une fonction intégrée qui affecte les générateurs
et qui leur demande d'aller jusqu'au rendement.
D'accord, si nous exécutons cette opération, vous pouvez voir que nous obtenons
zéro, parce que c'est le tout premier chiffre obtenu.
Notre générateur, "g", s'est arrêté
à la ligne 4. 5 entre 4 et 5, donc si
nous lui demandons un autre nombre, notez que bien sûr, à la fin d'un programme,
le générateur est détruit, donc si nous
exécutons à nouveau ce programme, ce sera à
nouveau zéro et ce sera un maintenant.
Il ne s'en souvient pas à travers les exécutions du programme,
ce n'est tout simplement pas la façon dont Python fonctionne.
Nous pouvons donc lui demander le premier chiffre,
puis le suivant.
Après le premier numéro, il s'arrêtera
entre quatre et cinq.
Lorsque nous disons next, cela continue jusqu'au prochain rendement,
ce qui signifie que nous répétons cette boucle une fois et que nous obtenons un.
D'accord.
Très important, la fonction se souvient de l'endroit où elle s'est arrêtée.
Ainsi, à tout moment, vous pouvez arrêter l'exécution
de la fonction, puis appeler next afin de la poursuivre.
Il n'est donc pas seulement utile pour générer des nombres ou autres, mais aussi
pour arrêter temporairement l'exécution d'une fonction,
afin de pouvoir continuer à l'exécuter
quand on le souhaite, en l'appelant
"next".
Ce n'est peut-être pas quelque chose qui a beaucoup
de sens pour l'instant, mais il faut s'en souvenir pour le moment où nous en
aurons besoin lorsque nous ferons une programmation synchronisée.
Qu'en est-il de la fonction de portée ?
J'ai dit tout à l'heure que cela expliquait
essentiellement comment la fonction d'intervalle fonctionne à l'intérieur, et c'est vrai,
mais la fonction d'intervalle
ne vous donne pas directement un générateur.
Ici, faisons quelque chose comme
"mon objet de plage", ce sera la
plage 10.
Cela se comporte très bien comme un générateur, mais
si vous appelez next de "my range object",
vous obtenez une erreur, et regardez ça.
C'est parce que l'objet range n'est pas tout à fait un générateur même
s'il se comporte de cette manière,
il ne génère pas une liste et vous permet ensuite d'itérer
dessus, il se comporte d'une manière qui s'apparente à une génération pour être plus
efficace.
"Vous vous souviendrez que
nous l'avons imprimé bien plus
tôt dans le cours et que nous
avons reçu un truc bizarre allant de zéro à dix.
C'est juste la fonction "repr" qui est appelée, et comme vous pouvez
le voir si nous imprimons le résultat de "repr", nous voyons
également une plage de zéro à dix.
Nous allons apprendre dans quelques
vidéos comment nous pouvons
itérer sur un objet pour qu'il se comporte comme
un objet de plage.
Nous avons donc appris à connaître ces générateurs, et une autre chose que
je voulais mentionner à propos des générateurs,
c'est que la fonction list peut transformer un générateur en liste.
Nous pouvons donc imprimer la liste des "g".
C'est ici que nous avons appelé "suivant", puis "suivant"
à nouveau, et enfin "liste".
Nous avons donc maintenant imprimé zéro et un, car ce sont les deux éléments
de nos fonctions "next", et nous avons
généré une liste du générateur, mais cette
liste reprend bien sûr là où nous nous sommes arrêtés.
Le générateur lui-même se souvient de ce qu'il
était, de sorte que la liste commence à partir de deux, et non de zéro.
Il est donc très important de se rappeler que les générateurs se souviennent
de l'endroit où ils se trouvaient.
Ainsi, lorsque vous en créez un, c'est essentiellement à partir du moment où vous commencez à l'utiliser, à partir
du moment où vous appelez "suivant",
il se souvient de l'endroit où il se trouve, et vous ne pouvez
pas revenir en arrière. D'accord.
Dans les prochaines vidéos, nous allons en
apprendre davantage sur les générateurs.
Nous allons apprendre à connaître les
classes de générateurs, comment vous pouvez créer une classe qui se comporte
comme un générateur, et nous allons également apprendre à connaître les itérateurs
et les itérables dans Python, et les objectifs de plage
en sont un exemple.
Nous passerons ensuite
à des exemples de programmation fonctionnelle,
tels que filter, map, any and all et enumerate.
Je suis donc très enthousiaste pour
cette section, et je suis convaincu que les générateurs seront quelque
chose dont vous aurez besoin dans votre développement Python,
en particulier au fur et à mesure que nous avançons dans le cours.
Sur ce, je vous donne rendez-vous pour la prochaine vidéo.