Einführung
In diesem Abschnitt wird untersucht, wie Sie die Iteration mithilfe einer Generatorfunktion anpassen können.
This tutorial is from open-source community. Access the source code
💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken
In diesem Abschnitt wird untersucht, wie Sie die Iteration mithilfe einer Generatorfunktion anpassen können.
Angenommen, Sie möchten Ihr eigenes benutzerdefiniertes Iterationsmuster erstellen.
Zum Beispiel eine Countdown.
>>> for x in countdown(10):
... print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
Es gibt einen einfachen Weg, dies zu tun.
Ein Generator ist eine Funktion, die die Iteration definiert.
def countdown(n):
while n > 0:
yield n
n -= 1
Beispiel:
>>> for x in countdown(10):
... print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
Ein Generator ist jede Funktion, die den yield
-Befehl verwendet.
Das Verhalten von Generatoren unterscheidet sich von dem einer normalen Funktion. Wenn man eine Generatorfunktion aufruft, wird ein Generator-Objekt erstellt. Die Funktion wird nicht sofort ausgeführt.
def countdown(n):
## Ein Print-Befehl hinzugefügt
print('Counting down from', n)
while n > 0:
yield n
n -= 1
>>> x = countdown(10)
## ES GIBT KEINEN PRINT-BEFEHL
>>> x
## x ist ein Generator-Objekt
<generator object at 0x58490>
>>>
Die Funktion wird nur bei einem Aufruf von __next__()
ausgeführt.
>>> x = countdown(10)
>>> x
<generator object at 0x58490>
>>> x.__next__()
Counting down from 10
10
>>>
yield
liefert einen Wert, aber suspendiert die Ausführung der Funktion. Die Funktion setzt bei dem nächsten Aufruf von __next__()
fort.
>>> x.__next__()
9
>>> x.__next__()
8
Wenn der Generator schließlich zurückkehrt, löst die Iteration einen Fehler aus.
>>> x.__next__()
1
>>> x.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in? StopIteration
>>>
Bemerkung: Eine Generatorfunktion implementiert das gleiche niedrigere Protokoll, das die for-Schleife bei Listen, Tupeln, Dictionaries, Dateien usw. verwendet.
Wenn Sie jemals merken, dass Sie die Iteration anpassen möchten, sollten Sie immer an Generatorfunktionen denken. Sie sind einfach zu schreiben - erstellen Sie eine Funktion, die die gewünschte Iterationslogik ausführt, und verwenden Sie yield
, um Werte auszugeben.
Beispielsweise versuchen Sie sich diesen Generator, der in einer Datei nach Zeilen mit einem passenden Substring sucht:
>>> 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
>>>
Dies ist ziemlich interessant - die Idee, dass Sie eine Menge an benutzerdefiniertem Prozessieren in einer Funktion verstecken können und sie verwenden, um einer for-Schleife Werte zu liefern. Das nächste Beispiel betrachtet einen ungewöhnlicheren Fall.
Generatoren können eine interessante Möglichkeit sein, Echtzeit-Datenquellen wie Log-Dateien oder Aktienmärkte zu überwachen. In diesem Teil werden wir diese Idee untersuchen. Beginnen Sie zunächst, indem Sie die folgenden Anweisungen genau befolgen.
Das Programm stocksim.py
ist ein Programm, das Aktienmarktdaten simuliert. Als Ausgabe schreibt das Programm ständig Echtzeitdaten in eine Datei stocklog.csv
. Öffnen Sie in einem separaten Befehlsfenster das Verzeichnis und führen Sie dieses Programm aus:
$ python3 stocksim.py
Wenn Sie unter Windows sind, finden Sie einfach die stocksim.py
-Datei und doppelklicken Sie darauf, um es auszuführen. Vergessen Sie nun dieses Programm (lassen Sie es einfach laufen). Öffnen Sie in einem anderen Fenster die von dem Simulator erstellte Datei stocklog.csv
. Sie sollten sehen, dass alle paar Sekunden neue Zeilen von Text zur Datei hinzugefügt werden. Lassen Sie dieses Programm ebenfalls im Hintergrund laufen - es wird mehrere Stunden lang laufen (Sie müssen sich nicht darum kümmern).
Wenn das obige Programm läuft, schreiben wir ein kleines Programm, um die Datei zu öffnen, zum Dateiende zu springen und nach neuer Ausgabe zu suchen. Erstellen Sie eine Datei follow.py
und fügen Sie diesen Code hinzu:
## follow.py
import os
import time
f = open('stocklog.csv')
f.seek(0, os.SEEK_END) ## Bewege den Dateizeiger um 0 Bytes vom Dateiende
while True:
line = f.readline()
if line == '':
time.sleep(0.1) ## Schlafe kurz und versuche es erneut
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}')
Wenn Sie das Programm ausführen, sehen Sie einen Echtzeit-Aktien-Ticker. Im Hintergrund ähnelt dieser Code der Unix-Befehl tail -f
, der verwendet wird, um eine Log-Datei zu überwachen.
Hinweis: Die Verwendung der readline()
-Methode in diesem Beispiel ist etwas ungewöhnlich, da es nicht die übliche Weise ist, Zeilen aus einer Datei zu lesen (normalerweise würden Sie einfach eine for
-Schleife verwenden). In diesem Fall verwenden wir es jedoch, um wiederholt das Ende der Datei zu überprüfen, um zu sehen, ob weitere Daten hinzugefügt wurden (readline()
gibt entweder neue Daten oder eine leere Zeichenfolge zurück).
Wenn Sie sich den Code in Übung 6.5 ansehen, produziert der erste Teil des Codes Zeilen von Daten, während die Anweisungen am Ende der while
-Schleife die Daten verarbeiten. Ein wichtiges Merkmal von Generatorfunktionen ist, dass Sie den gesamten Datenproduktionscode in eine wiederverwendbare Funktion verschieben können.
Ändern Sie den Code in Übung 6.5 so, dass das Lesen der Datei von einer Generatorfunktion follow(filename)
durchgeführt wird. Stellen Sie sicher, dass der folgende Code funktioniert:
>>> for line in follow('stocklog.csv'):
print(line, end='')
... Sollte hier Zeilen von Ausgabe sehen...
Ändern Sie den Aktien-Ticker-Code so, dass er wie folgt aussieht:
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}')
Ändern Sie das Programm follow.py
so, dass es den Strom von Aktiendaten beobachtet und einen Ticker druckt, der nur Informationen für die Aktien in einem Portfolio anzeigt. Beispielsweise:
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}')
Hinweis: Damit dies funktioniert, muss Ihre Portfolio
-Klasse den in
-Operator unterstützen. Siehe Übung 6.3 und stellen Sie sicher, dass Sie den __contains__()
-Operator implementieren.
Etwas sehr Potentes ist gerade passiert. Sie haben ein interessantes Iterationsmuster (das Lesen von Zeilen am Ende einer Datei) in seine eigene kleine Funktion verlagert. Die follow()
-Funktion ist jetzt diese völlig allzweckdienliche Utility, die Sie in jedem Programm verwenden können. Beispielsweise könnten Sie sie verwenden, um Server-Logs, Debugging-Logs und andere ähnliche Datenquellen zu beobachten. Das ist ziemlich cool.
Herzlichen Glückwunsch! Sie haben das Lab zu Iterationsanpassungen abgeschlossen. Sie können in LabEx weitere Labs ausprobieren, um Ihre Fähigkeiten zu verbessern.