Abschnitt 8 · Lektion 7
Sortieren mit eigenem Schlüssel
sorted sortiert Zahlen der Größe nach und Text nach dem Alphabet, mehr oder weniger. Seit
Abschnitt 7 sehen Daten aber anders aus, als Liste von Zeilen, jede ein Dictionary. Wonach soll
Python die sortieren?
ausgaben = [
{"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
{"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
{"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
]
print(sorted(ausgaben)) Python weiß es nicht und sagt das auch:
TypeError: '<' not supported between instances of 'dict' and 'dict'. Kleiner als, also <, geht
nicht zwischen zwei Dictionaries.
Zum Sortieren muss Python bei zwei Einträgen entscheiden können, welcher zuerst kommt. Ob eine Ausgabe vom 28.09. kleiner ist als eine für Freizeit, kann aber niemand sagen. Du musst es Python sagen, etwa nach dem Betrag, dem Datum oder der Kategorie.
Eine Funktion als Schlüssel
def betrag(ausgabe):
return ausgabe["betrag"]
ausgaben = [
{"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
{"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
{"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
{"datum": "02.10.", "kategorie": "Freizeit", "betrag": 7.50},
{"datum": "03.10.", "kategorie": "Lebensmittel", "betrag": 18.60},
]
for ausgabe in sorted(ausgaben, key=betrag):
print(f"{ausgabe['betrag']:6.2f} {ausgabe['datum']} {ausgabe['kategorie']}")
print("Die Liste selbst ist unverändert:", ausgaben[0]["betrag"]) Gesagt wird es mit einer Funktion. betrag bekommt eine Zeile und gibt ihren Betrag zurück. Mehr
nicht. Diese Funktion bekommt sorted als benanntes Argument key, den Sortierschlüssel, also das,
wonach sortiert wird. Mit dem Schlüssel eines Dictionarys hat er nur den Namen gemeinsam.
Für jeden Eintrag ruft sorted die Funktion einmal auf, sortiert nach dem, was zurückkommt, und
liefert die Einträge selbst in dieser Reihenfolge. Die Zeilen bleiben ganz, Datum und Kategorie
reisen mit, und sortiert sind sie nach dem Betrag, von 2,90 bis 31,80 Euro. Die Liste ausgaben
selbst ist danach unverändert, denn sorted baut eine neue.
Hinter key=betrag stehen keine Klammern, und das ist Absicht. betrag() würde die Funktion sofort
aufrufen, ohne Zeile, und das gäbe einen TypeError. betrag ohne Klammern ist die Funktion selbst,
und die gibst du sorted in die Hand, damit es sie später für jeden Eintrag aufruft.
Die zwei kleinsten
def betrag(ausgabe):
return ausgabe["betrag"]
ausgaben = [
{"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
{"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
{"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
{"datum": "02.10.", "kategorie": "Freizeit", "betrag": 7.50},
{"datum": "03.10.", "kategorie": "Lebensmittel", "betrag": 18.60},
]
for ausgabe in sorted(ausgaben, key=betrag)[:2]:
print(f"{ausgabe['betrag']:6.2f} {ausgabe['datum']} {ausgabe['kategorie']}") Weil sorted eine Liste liefert, kannst du davon einen Ausschnitt nehmen. [:2] sind die ersten
beiden, also die zwei kleinsten Ausgaben der Woche. Gäbe es weniger Einträge als verlangt, wäre das
kein Problem, denn ein Ausschnitt, der zu weit reicht, hört einfach früher auf. Mit reverse=True
(umgekehrt) geht es andersherum. Die Reihenfolge dreht sich um, und die größten Beträge kommen
zuerst.
Kurz mit lambda
ausgaben = [
{"datum": "28.09.", "kategorie": "Lebensmittel", "betrag": 31.80},
{"datum": "29.09.", "kategorie": "Freizeit", "betrag": 18.00},
{"datum": "30.09.", "kategorie": "Mobilität", "betrag": 2.90},
{"datum": "02.10.", "kategorie": "Freizeit", "betrag": 7.50},
{"datum": "03.10.", "kategorie": "Lebensmittel", "betrag": 18.60},
]
nach_datum = sorted(ausgaben, key=lambda a: a["datum"])
print([a["datum"] for a in nach_datum])
nach_datum = sorted(ausgaben, key=lambda a: a["datum"][3:5] + a["datum"][0:2])
print([a["datum"] for a in nach_datum]) Eine Funktion wie betrag, die man nur für ein einziges sorted braucht, ist fast mehr
Schreibarbeit als das Sortieren selbst. Die Kurzform dafür heißt lambda. lambda a: a["datum"]
ist eine Funktion ohne Namen. Vor dem Doppelpunkt steht der Parameter, hier a, dahinter der Wert,
der zurückkommt.
Ein return steht nicht da. Hinter dem Doppelpunkt darf nur genau ein Wert stehen oder eine
Rechnung, die einen ergibt, und der ist dann das Ergebnis. Kein if, keine Schleife, keine zweite
Zeile. Für alles, was mehr braucht, nimmt man ein def.
Im Beispiel wird nach dem Datum sortiert, und dabei siehst du gleich, wie man sich vertun kann. Die
erste Liste beginnt mit dem 02.10., dann kommt der 03.10. und erst danach der September. Ein Datum
wie 28.09. ist für Python Text, und Text wird Zeichen für Zeichen verglichen. Dabei kommt 0 vor
2, egal welcher Monat dahinter steht.
Im zweiten Sortierschlüssel wird deshalb mit Ausschnitten aus jedem Datum ein neuer Text gebaut,
erst der Monat, dann der Tag. Aus 28.09. wird 0928, aus 02.10. wird 1002, und so sortiert
stimmt die Reihenfolge. Sortiert wird nach dem, was der Sortierschlüssel zurückgibt, und das darf
alles sein, was sich vergleichen lässt.
Das Größte samt seiner Zeile
preise = {"Milch": 1.19, "Brot": 2.49, "Eier": 3.29, "Butter": 2.29}
print(max(preise.values()))
print(max(preise.items(), key=lambda paar: paar[1]))
for artikel, preis in sorted(preise.items(), key=lambda paar: paar[1], reverse=True):
print(f"{artikel:<8}{preis:5.2f}") max und min nehmen dasselbe key. In Lektion 7.2 blieb eine Frage offen, nämlich welcher
Artikel der teuerste ist. max(preise.values()) sagt nur, dass es 3,29 Euro sind. items() liefert
die Paare aus Artikel und Preis. Mit dem Preis als Sortierschlüssel, paar[1], gibt max das ganze
Paar zurück, ('Eier', 3.29). Genauso sortiert sorted die Paare nach dem Preis statt nach dem
Artikel.
In Abschnitt 7 hat sich eine Schleife die ganze Zeile gemerkt, um die größte Ausgabe samt Datum zu
finden. Mit max(ausgaben, key=lambda a: a["betrag"]) ist das eine Zeile, und heraus kommt dasselbe
Dictionary.
Nach deutschem Alphabet
def deutsch(wort):
wort = wort.lower()
return wort.replace("ä", "a").replace("ö", "o").replace("ü", "u").replace("ß", "ss")
woerter = ["Zucker", "apfel", "Öl", "Birne", "Äpfel", "eier"]
print(sorted(woerter))
print(sorted(woerter, key=lambda wort: wort.lower()))
print(sorted(woerter, key=deutsch)) In Lektion 5.4 hat Python apfel hinter Zitrone sortiert und die Äpfel ganz ans Ende. Dort
stand auch, dass man ihm eine eigene Reihenfolge beibringen kann, und hier ist sie.
Mit lower() als Sortierschlüssel werden Groß und Klein gleich behandelt. Nur die Umlaute stehen
dann noch hinter dem Z. Die Funktion deutsch macht deshalb aus jedem Wort eine Fassung zum
Vergleichen, klein geschrieben, ä zu a, ö zu o, ü zu u und ß zu ss. Das ist ungefähr die Regel, nach
der ein Wörterbuch sortiert. Die Wörter selbst bleiben, wie sie sind. Verglichen wird nur über ihre
Fassung, und Äpfel steht jetzt neben apfel.
Was die Aufgabe will
Das Haushaltsbuch aus Abschnitt 7 noch einmal, mit derselben Woche. Gesucht sind die drei größten Ausgaben, die größte zuerst. Jede steht mit ihrer Platzierung, ihrem Datum und ihrer Kategorie da.
Zum Mitnehmen
Mit key= bekommen sorted, max und min eine Funktion, die für jeden Eintrag den Wert liefert, nach dem sortiert wird. So sortiert key=lambda a: a["betrag"] Zeilen nach ihrem Betrag. Die Einträge bleiben dabei ganz, nur ihre Reihenfolge ändert sich, und mit reverse=True dreht sie sich um.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 6 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.