Für meine Plotroutinen brauche ich oft Funktionen, die eine Liste an Messwerten durchgehen und nur diejenigen zurückgeben, die einem bestimmten Kriterium entsprechen. Wenn dabei die Anzahl der Messwerte in die Millionen geht, kann alleine schon die Liste der ungefilterten Messwerte den Arbeitsspeicher des Rechners sprengen.
Ich könnte jetzt einfach eine Funktion schreiben, die alle Werte liest, filtert und nur die Relevanten zurückgibt.
Das könnte dann etwa so aussehen:
def naivefilter(filepath, name): """Get only the relevant values.""" values = [] with open(filepath) as f: for line in f: if line.startswith(name): values.append(line) return values
Wirkt im Kleinen noch übersichtlich. Aber sobald wir die Anzahl der Filterkriterien erhöhen - oder andere Eingabewerte brauchen - wird die Funktion unübersichtlich. Und unübersichtlicher Code ist unschön zu warten.
Um das Problem zu minimieren, nutze ich Iteratoren, die nur jeweils die benötigten Werte im Speicher zu behalten, aber von Funktion zu Funktion weitergegeben werden können. Damit kann ich die Logik zur Auswahl der Werte aus der auslesenden Funktion heraushalten und erhalte damit zwei schlanke Funktionen.
Das ganze sieht dann beispielsweise so aus:
def allvalues(filepath): """Get all values from the file.""" with open(filepath) as f: for line in f: yield line
def valuesforstation(filepath, name): """Get all the values from a file which come from the given station.""" return tuple(line for line in allvalues(filepath) if line.startswith(name))
Wirkt anfangs etwas länger, aber was passiert, wenn wir ein anderes Filterkriterium haben wollen?
Dann fügen wir einfach eine andere Filterfunktion hinzu:
def valueswithoutX(filepath, X): """Get all the values from a file which do not contain the value X.""" return tuple(line for line in allvalues(filepath) if not X in line)
Damit haben wir das Auslesen und das Filtern der Daten sauber getrennt, ohne jemals alle Werte gleichzeitig im Speicher halten zu müssen.
Der Code ist wartbar, übersichtlich und effizient. Und damit ist er genau so, wie ich ihn haben will.
Und falls ihr euch fragt, warum ich am Ende einen tuple zurückgebe und keinen weiteren Iterator: Ich habe noch einen Caching-Dekorator, und der mag keine Iteratoren (weil die theoretisch unendlich viele Werte zurückgeben können und damit nicht immer gecacht werden können). Aber dazu kommen wir ein ander’ Mal…1 (bis dahin kann ich euch den Leitfaden zu Dekoratoren von Simon Franklin empfehlen, wenn ihr mit fortgeschritteneren Techniken experimentieren wollt :) ).
PS: Ja, ich könnte auch eine Datenbank nutzen. Dann muss ich die allerdings füllen und aktuell halten und wäre so weiter weg von den Daten, auf denen ich eigentlich arbeite. Und die Gesamt-Aufgaben sind schon komplex genug, ohne einen weiteren Indirektionsschritt einzuführen…
PPS: Alle Codeschnipsel in Python, farbige Quelltext-Hervorhebung via M-x htmlize-region
in emacs mit inline-css (einstellbar über M-x customize-variable htmlize-output-type
)
Soll heißen, dazu kommen wir vielleicht ein ander’ Mal, je nach meinem Zeitbudget… ↩
Use Node:
⚙ Babcom is trying to load the comments ⚙
This textbox will disappear when the comments have been loaded.
If the box below shows an error-page, you need to install Freenet with the Sone-Plugin or set the node-path to your freenet node and click the Reload Comments button (or return).
If you see something like Invalid key: java.net.MalformedURLException: There is no @ in that URI! (Sone/search.html)
, you need to setup Sone and the Web of Trust
If you had Javascript enabled, you would see comments for this page instead of the Sone page of the sites author.
Note: To make a comment which isn’t a reply visible to others here, include a link to this site somewhere in the text of your comment. It will then show up here. To ensure that I get notified of your comment, also include my Sone-ID.
Link to this site and my Sone ID: sone://6~ZDYdvAgMoUfG6M5Kwi7SQqyS-gTcyFeaNN1Pf3FvY
This spam-resistant comment-field is made with babcom.