Python-Kurs / Listen, Wörterbücher und Mengen mit Comprehensions erstellen

Eine Comprehension erzeugt eine neue Sammlung, indem sie vorhandene Elemente umwandelt oder nach einer Bedingung auswählt. Verwenden Sie diese Schreibweise für überschaubare Regeln; bei mehreren Verarbeitungsschritten, gezielter Fehlerbehandlung oder reinen Aktionen ist eine normale Schleife meist verständlicher.

Umwandlung und Auswahl auseinanderhalten

Eine Listen-Comprehension hat die Form [Ausdruck for Element in Quelle if Bedingung]. Lesen Sie zuerst den for-Teil: Er liefert die Elemente. Die abschließende Bedingung wählt daraus aus. Nur für ein ausgewähltes Element wird der vordere Ausdruck ausgewertet und sein Ergebnis in die neue Liste aufgenommen. [x * 2 for x in range(4) if x % 2 == 0] wählt 0 und 2 und liefert deshalb [0, 4].

Die bedingte Auswahl eines Wertes steht an einer anderen Position: "paid" if x > 0 else "free" erzeugt für jedes bereits zugelassene Element einen von zwei Werten. Sie entfernt kein Element. Der Unterschied ist praktisch: Ein nachgestelltes if kann die Länge verkürzen; ein bedingter Ergebnisausdruck vergibt stattdessen für jedes durchgelassene Element einen Wert.

Die äußere Quellsammlung wird durch diese Syntax nicht automatisch geändert. Das bedeutet jedoch keine vollständige Kopie ihrer Inhalte: Wenn der Ergebnisausdruck ein vorhandenes Wörterbuch oder eine vorhandene Liste übernimmt, bleibt dieses Objekt geteilt. Auch ausdrücklich im Ausdruck ausgeführte Methoden können Nebenwirkungen haben. Die Einführung zu Listen-Comprehensions erläutert die Schreibweise.

Listen, Wörterbücher und Mengen erzeugen

Speichern Sie den vollständigen Code als comprehensions.py in UTF-8 im Übungsordner. Starten Sie ihn mit python comprehensions.py im Projektinterpreter. Außerhalb einer aktivierten virtuellen Umgebung kann unter macOS oder Ubuntu python3 erforderlich sein. Das Beispiel benötigt nur Python und keine Eingabedateien oder Zusatzpakete.

amounts = [3500, -1, 8000, 0]
valid = [x for x in amounts if x >= 0]
labels = ["paid" if x > 0 else "free" for x in valid]
records = [("food", 3500), ("bus", 2500)]
by_name = {name: amount for name, amount in records}
remainders = {x % 3 for x in valid}
print(valid)
print(labels)
print(by_name)
print(sorted(remainders))

Erwartete Ausgabe:

[3500, 8000, 0]
['paid', 'paid', 'free']
{'food': 3500, 'bus': 2500}
[0, 2]

valid enthält die drei nichtnegativen Beträge einschließlich 0 in ihrer ursprünglichen Reihenfolge. Die zweite Liste beschreibt dieselben drei Positionen mit zwei englischen Statuswerten: Die positiven Beträge sind paid, die Null ist free. by_name ordnet jedem Namen seinen Betrag zu.

Die geschweiften Klammern mit Doppelpunkt zwischen Schlüssel und Wert erzeugen ein Wörterbuch; ohne diesen Doppelpunkt erzeugt der letzte Ausdruck eine Menge. Sowohl 3500 als auch 8000 haben beim Teilen durch 3 den Rest 2, die Null hat Rest 0. Daher enthält remainders nur zwei verschiedene Werte. Erst sorted macht für die Ausgabe daraus die verlässlich geordnete Liste [0, 2]; die Menge selbst hat keine zugesicherte Anzeigereihenfolge. Die Grundlagen der Iteration mit for bleiben bei allen drei Formen gleich.

Doppelte Schlüssel überschreiben statt addieren

Fügen Sie am Ende von records ein weiteres Paar ("food", 1000) ein. Im erzeugten Wörterbuch steht für food anschließend 1000; 3500 und 1000 werden nicht zu 4500 zusammengezählt. Ein späterer Wert ersetzt den früheren Wert desselben Schlüssels. Die Position eines bereits vorhandenen Schlüssels wird durch das Ersetzen nicht neu ans Ende verschoben.

Wenn wiederholte Kategorien addiert werden sollen, ist eine normale Akkumulationsschleife nötig: Starten Sie mit einem leeren Wörterbuch, lesen Sie für jede Kategorie den bisherigen Betrag mit get(category, 0) und speichern Sie alten Betrag plus neuen Betrag. Die Übung zu Summen mit Wörterbüchern zeigt diese Verarbeitung. Eine kurze Schlüssel-Wert-Comprehension ersetzt diese Summenbildung nicht.

Bei Mengen ist das Zusammenfallen gleicher Ergebnisse gewollt. Eine Mengen-Comprehension ist daher keine geeignete Ergebnisliste, wenn jede ursprüngliche Buchung oder ihre Reihenfolge erhalten bleiben muss. Wörterbuchschlüssel und Mengenelemente müssen zudem hashbar sein: Eine veränderliche Liste eignet sich dafür nicht. Die Sprachreferenz zu Sammlungs-Ausdrücken beschreibt die jeweiligen Formen.

Leere Ergebnisse und geteilte Objekte prüfen

  1. Ändern Sie nur den Filter x >= 0 in x > 0. Nun sind valid und labels jeweils zwei Elemente lang; die Null und damit das Label free verschwinden. Die Restmenge enthält nur 2.
  2. Ersetzen Sie die Beträge durch [-1, -2]. Es entstehen eine leere Ergebnisliste, eine leere Labelliste und eine leere Restmenge; die unabhängig definierten Namen und Beträge in records bleiben davon unberührt.
  3. Prüfen Sie vor valid[0] oder einer Durchschnittsberechnung, ob Werte vorhanden sind. Ein leerer Indexzugriff verursacht IndexError, eine Division durch die leere Länge ZeroDivisionError. Entscheiden Sie ausdrücklich, ob ein leerer Fall eine Meldung oder einen anderen Rückgabewert erhalten soll.
  4. Stellen Sie die ursprünglichen Eingaben und den Filter wieder her und vergleichen Sie alle vier Ausgabezeilen mit der erwarteten Ausgabe.

Um die gemeinsame Referenz zu verstehen, betrachten Sie source = [{"amount": 3500}] und selected = [row for row in source]. selected ist eine neue Liste, aber selected[0] und source[0] sind dasselbe Wörterbuch. Eine Änderung seines Betrags ist deshalb über beide Listen sichtbar. Wenn Sie dagegen nur die Zahlen mit [row["amount"] for row in source] übernehmen, enthält die Ergebnisliste diese Zahlenwerte statt des Wörterbuchs.

Verwenden Sie keine Listen-Comprehension allein zum Drucken oder Speichern von Dateien. Eine Form wie [print(x) for x in valid] erzeugt zusätzlich eine meist ungenutzte Liste aus None-Werten. Schreiben Sie dafür eine gewöhnliche Schleife. Auch verschachtelte Filter sollten Sie zuerst als nachvollziehbare Schleifen formulieren und nur dann kürzen, wenn die Regel dadurch leichter lesbar bleibt.