Introduction
Cette section examine la manière dont vous pouvez personnaliser l'itération à l'aide d'une fonction génératrice.
This tutorial is from open-source community. Access the source code
💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici
Cette section examine la manière dont vous pouvez personnaliser l'itération à l'aide d'une fonction génératrice.
Supposons que vous vouliez créer votre propre modèle d'itération personnalisé.
Par exemple, une mise à jour à rebours.
>>> for x in countdown(10):
... print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
Il existe un moyen facile de le faire.
Un générateur est une fonction qui définit une itération.
def countdown(n):
while n > 0:
yield n
n -= 1
Par exemple :
>>> for x in countdown(10):
... print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
Un générateur est toute fonction qui utilise l'instruction yield
.
Le comportement des générateurs est différent d'une fonction normale. Appeler une fonction génératrice crée un objet générateur. Elle n'exécute pas immédiatement la fonction.
def countdown(n):
## Ajout d'une instruction print
print('Compte à rebours depuis', n)
while n > 0:
yield n
n -= 1
>>> x = countdown(10)
## Il n'y a AUCUNE INSTRUCTION PRINT
>>> x
## x est un objet générateur
<generator object at 0x58490>
>>>
La fonction n'est exécutée que lors de l'appel à __next__()
.
>>> x = countdown(10)
>>> x
<generator object at 0x58490>
>>> x.__next__()
Compte à rebours depuis 10
10
>>>
yield
produit une valeur, mais suspend l'exécution de la fonction. La fonction reprend lors du prochain appel à __next__()
.
>>> x.__next__()
9
>>> x.__next__()
8
Lorsque le générateur renvoie finalement, l'itération lève une erreur.
>>> x.__next__()
1
>>> x.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in? StopIteration
>>>
Attention : Une fonction génératrice implémente le même protocole de bas niveau que les instructions for utilisées sur les listes, les tuples, les dictionnaires, les fichiers, etc.
Si vous vous trouvez à avoir besoin de personnaliser une itération, vous devriez toujours penser aux fonctions génératrices. Elles sont faciles à écrire - créez une fonction qui exécute la logique d'itération souhaitée et utilisez yield
pour émettre des valeurs.
Par exemple, essayez ce générateur qui recherche dans un fichier les lignes contenant une sous-chaîne correspondante :
>>> def filematch(filename, substr):
with open(filename, 'r') as f:
for line in f:
if substr in line:
yield line
>>> for line in open('portfolio.csv'):
print(line, end='')
name,shares,price
"AA",100,32.20
"IBM",50,91.10
"CAT",150,83.44
"MSFT",200,51.23
"GE",95,40.37
"MSFT",50,65.10
"IBM",100,70.44
>>> for line in filematch('portfolio.csv', 'IBM'):
print(line, end='')
"IBM",50,91.10
"IBM",100,70.44
>>>
C'est assez intéressant - l'idée que vous pouvez cacher un certain traitement personnalisé dans une fonction et l'utiliser pour alimenter une boucle for
. L'exemple suivant examine un cas plus inhabituel.
Les générateurs peuvent être un moyen intéressant de surveiller des sources de données en temps réel telles que des fichiers de journalisation ou des flux de marché boursier. Dans cette partie, nous allons explorer cette idée. Pour commencer, suivez attentivement les instructions suivantes.
Le programme stocksim.py
est un programme qui simule des données de marché boursier. En sortie, le programme écrit constamment des données en temps réel dans un fichier stocklog.csv
. Dans une fenêtre de commande séparée, accédez au répertoire `` et exécutez ce programme :
$ python3 stocksim.py
Si vous êtes sous Windows, localisez simplement le programme stocksim.py
et double-cliquez sur lui pour l'exécuter. Maintenant, oubliez ce programme (laissez-le simplement s'exécuter). En utilisant une autre fenêtre, regardez le fichier stocklog.csv
qui est écrit par le simulateur. Vous devriez voir de nouvelles lignes de texte ajoutées au fichier toutes les quelques secondes. Encore une fois, laissez simplement ce programme s'exécuter en arrière-plan - il s'exécutera pendant plusieurs heures (vous n'aurez pas besoin de vous en soucier).
Une fois le programme ci-dessus en cours d'exécution, écrivons un petit programme pour ouvrir le fichier, aller à la fin et surveiller de nouveaux résultats. Créez un fichier follow.py
et mettez ce code dedans :
## follow.py
import os
import time
f = open('stocklog.csv')
f.seek(0, os.SEEK_END) ## Décale le pointeur du fichier de 0 octets depuis la fin du fichier
while True:
line = f.readline()
if line == '':
time.sleep(0.1) ## Dodo brièvement et réessayez
continue
fields = line.split(',')
name = fields[0].strip('"')
price = float(fields[1])
change = float(fields[4])
if change < 0:
print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
Si vous exécutez le programme, vous verrez un cotation boursière en temps réel. Sous le capot, ce code est un peu comme la commande Unix tail -f
qui est utilisée pour surveiller un fichier de journalisation.
Remarque : L'utilisation de la méthode readline()
dans cet exemple est un peu inhabituelle car ce n'est pas la manière habituelle de lire les lignes d'un fichier (normalement, vous utiliseriez simplement une boucle for
). Cependant, dans ce cas, nous l'utilisons pour interroger régulièrement la fin du fichier pour voir si de nouvelles données ont été ajoutées (readline()
renverra soit de nouvelles données soit une chaîne vide).
Si vous examinez le code de l'Exercice 6.5, la première partie du code produit des lignes de données tandis que les instructions à la fin de la boucle while
consomment les données. Une caractéristique majeure des fonctions génératrices est que vous pouvez déplacer tout le code de production de données dans une fonction réutilisable.
Modifiez le code de l'Exercice 6.5 de sorte que la lecture du fichier soit effectuée par une fonction génératrice follow(filename)
. Assurez-vous que le code suivant fonctionne :
>>> for line in follow('stocklog.csv'):
print(line, end='')
... Devrait voir des lignes de sortie produites ici...
Modifiez le code de la cotation boursière de sorte qu'il ressemble à ceci :
if __name__ == '__main__':
for line in follow('stocklog.csv'):
fields = line.split(',')
name = fields[0].strip('"')
price = float(fields[1])
change = float(fields[4])
if change < 0:
print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
Modifiez le programme follow.py
de sorte qu'il surveille le flux de données boursières et affiche une cotation montrant les informations seulement pour les actions d'un portefeuille. Par exemple :
if __name__ == '__main__':
import report
portfolio = report.read_portfolio('portfolio.csv')
for line in follow('stocklog.csv'):
fields = line.split(',')
name = fields[0].strip('"')
price = float(fields[1])
change = float(fields[4])
if name in portfolio:
print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
Remarque : Pour que cela fonctionne, votre classe Portfolio
doit supporter l'opérateur in
. Consultez l'Exercice 6.3 et assurez-vous d'avoir implémenté l'opérateur __contains__()
.
Quelque chose de très puissant vient de se produire ici. Vous avez déplacé un modèle d'itération intéressant (la lecture de lignes à la fin d'un fichier) dans sa propre petite fonction. La fonction follow()
est désormais cette utilité complètement générique que vous pouvez utiliser dans n'importe quel programme. Par exemple, vous pourriez l'utiliser pour surveiller les journaux de serveur, les journaux de débogage et d'autres sources de données similaires. C'est assez cool.
Félicitations ! Vous avez terminé le laboratoire sur la personnalisation de l'itération. Vous pouvez pratiquer d'autres laboratoires dans LabEx pour améliorer vos compétences.