Iteratoren und Generatoren : Wie man Daten verarbeitet, ohne den Speicher zu belasten

Iteratoren und Generatoren : Wie man Daten verarbeitet, ohne den Speicher zu belasten
3 Minuten Lesezeit

Ein Skript zur Zusammenfassung des Zahlungsjournals lief ein Jahr lang problemlos, bevor es an einer zu großen Datei scheiterte. Ein Tutorial zu Pythons Iteratoren und Generatoren zeigt, wie man Verarbeitungsketten aufbaut, die Daten Satz für Satz lesen, ohne sie alle auf einmal in den Speicher zu laden.

  • Das Skript scheiterte, weil eine Liste alle Datensätze gleichzeitig im Speicher hielt.
  • Iteratoren und Generatoren verarbeiten Daten Satz für Satz, ohne die gesamte Datei zu laden.
  • Aufrufe wie list() oder sorted() bringen den gesamten Datenstrom wieder in den Speicher.
  • Globale Sortierungen und exakte Deduplizierungen erfordern andere Verfahren oder externe Werkzeuge.

Im beschriebenen Fall öffnete das Skript die Datei, baute eine Liste der analysierten Datensätze auf, filterte Fehler heraus und gab dann eine Tabelle aus. Nach einer Änderung bei der Dateirotation landete eine Woche Verkehr in einer einzigen 9-GB-Datei. Die nächtliche Verarbeitung brach mit einem MemoryError ab, bevor sie auch nur eine Zusammenfassung erzeugt hatte. Laut Autor lag das Problem nicht in der Analyselogik: Es war die Liste, die zwang, alle Datensätze gleichzeitig im Speicher zu halten.

Die Funktionsweise beruht auf einem einfachen Vertrag. Ein Iterable, wie eine Liste, eine Zeichenkette oder eine geöffnete Datei, kann einen Iterator bereitstellen, also einen Cursor, der Element für Element voranschreitet und nie zurückkehrt. Die for-Schleife ruft diese Schritte automatisch auf, und das Ende der Sequenz wird durch die Ausnahme StopIteration signalisiert, die kein Fehler ist. Ein erschöpfter Iterator setzt sich nicht zurück: Eine Funktion, die bei einem zweiten Durchlauf « nichts » zurückgibt, ist nicht zwangsläufig leer, sondern wurde einfach bereits verbraucht.

Das Tutorial empfiehlt, mit dem Dateiobjekt zu beginnen, das bereits ein Iterator ist. Eine Datei mit f.readlines() zu lesen erzeugt eine Liste aller Zeilen, während for line in f nur einen gepufferten Block auf einmal liest. Generatorfunktionen, die das Schlüsselwort yield verwenden, erlauben es dann, die Analyse in benannte Schritte zu unterteilen. Die Anweisung yield from verkettet mehrere Rotationsdateien wie einen einzigen Datenstrom, und Generatorausdrücke, in Klammern geschrieben, dienen kleinen Zwischentransformationen. Der Autor weist auf eine Falle hin: Der am weitesten links stehende Teil eines Generatorausdrucks wird sofort ausgewertet.

Messen, bevor man vertraut

Das Tutorial betont auch die Stellen, an denen die Liste unauffällig zurückkehrt. Aufrufe wie list(), sorted(), ein Wörterbuch, das Datensätze nach Anfrage-ID gruppiert, oder die Erstellung eines DataFrame führen den gesamten Datenstrom wieder in den Speicher ein. Generatoren sind auch nicht schneller als Listen: Ihr Vorteil liegt in begrenztem Speicherverbrauch und umstellbaren Verarbeitungsschritten. Für Stapelverarbeitungen bietet Python 3.12 itertools.batched(), und für frühere Versionen wird ein entsprechendes Rezept mit islice angegeben.

Um das tatsächliche Verhalten zu überprüfen, empfiehlt der Autor das Modul tracemalloc, das den von Python zugewiesenen Speicher misst, sowohl aktuell als auch im Spitzenwert. Bei einem synthetischen Journal mit 200.000 Zeilen (ca. 17 MB) erreichte die « Listen »-Version auf einem Laptop einen Spitzenwert von 198 MB, während die Streaming-Version bei 0,4 MB lag. Der Autor präzisiert, dass es sich um Ergebnisse eines einzelnen Durchlaufs handelt, nicht um einen Benchmark. Er erinnert auch daran, dass tracemalloc weder Zuweisungen von C-Erweiterungen noch den System-Cache sieht.

Die Grenzen des Datenstroms

Einige Verarbeitungen lassen sich nicht im Datenstrom durchführen. Eine globale Sortierung, eine exakte Deduplizierung über die gesamte Datei oder ein Perzentil, das über alle Latenzwerte berechnet wird, erfordern die Kenntnis aller Datensätze. In diesem Fall erwähnt das Tutorial einen approximativen Algorithmus auf Basis einer Stichprobe oder ein externes Tool, das auf der Festplatte sortiert.

Das Tutorial stellt abschließend klar, dass dieser Ansatz in reinem Python bleibt. Pandas, Spark, Dask oder asynchrone Datenströme lösen ähnliche Probleme, sind aber für eine Journaldatei, einen Export oder einen Daten-Dump, der auf die Festplatte passt, aber nicht in den Arbeitsspeicher, nicht erforderlich.

S’abonner
Notification pour
guest

0 Commentaires
Le plus ancien
Le plus récent Le plus populaire
0
Nous aimerions avoir votre avis, veuillez laisser un commentaire.x