Sortieren in Python
Ziel
Ich kenne die Sortiermechanismen von Python und kann sie auch dann anwenden, wenn eine besondere Sortierordnung gefragt ist.
Hintergrund
Sortieren braucht man beim Programmieren ziemlich oft. Wer in normalen Anwendungen selber ein Sortierverfahren implementiert, hat wahrscheinlich nicht mehr alle Tassen im Schrank, denn es gibt in jeder ernstzunehmenden Sprache gute Implementierungen, die man wiederverwenden sollte.
Fast alle Aufgaben kann man mit den "eingebauten" Sortierverfahren gut lösen, wenn man weiß, wie man sie richtig benutzt.
Und selbst, wenn das mal nicht geht, ist wahrscheinlich eine andere Standardimplementierung die beste Alternative; siehe z.B. die Aufgabe m_subprocess2 für ein eindrucksvolles Beispiel.
Hier lernen wir also das nötige Grundwissen über Sortieren in Python.
Arbeitsschritte
Startpunkt
- 1 Legen Sie die Datei
sorted_and_sort.py
an und fügen Sie Folgendes dort ein:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
Diese vier verschiedenen Sortierungen erarbeiten wir uns nun.
- Lesen Sie über die eingebaute Funktion
sorted
nach: https://docs.python.org/3/library/functions.html#sorted - Damit kann man offensichtlich sehr einfach eine sortierte Kopie(!) einer Liste von Integers oder einer Liste von Strings erhalten.
- Sortiert man damit Tupel wie in unserem Fall, dann ist deren erstes Element das Sortierkriterium. Wenn das gleich ist, gilt das zweite und so weiter.
- 2 Geben Sie bei Schritt 1 eine sortierte Kopie von
data
aus. - 3 Und wenn wir nach Körpergröße sortieren müssen?
Geht auch. Dafür ist das Argument
key
geeignet. Wir brauchen eine Funktion, die die Körpergröße aus einem Tupel fischt. Schreiben Sie eine Funktionheight(mytuple: tuple[int, int, int]) -> int
, die das tut, übergeben Sie sie beikey
und geben Sie bei Schritt 2 die so sortierte Liste von Tupeln aus.
Hinweis (nur bei Bedarf): Warum bekomme ich dabei immer eine Fehlermeldung?
Sie müssen die Funktion übergeben, also key=height
,
nicht das Ergebnis eines Aufrufs davon, wie bei key=height()
u.ä.
- Dieser Fall, dass die
key
-Funktion nur ein vorhandenes Element aus einem komplexen Objekt fischen muss, ist so häufig, dass es dafür zwei fertige Helferlein in Python gibt.operator.itemgetter
(um dask
-te Element einer Sequenz zu holen; das ist unser Fall) undoperator.attrgetter
(um ein Attribut mit dem Namenname
zu holen).
Lesen Sieitemgetter
nach: https://docs.python.org/3/library/operator.html#operator.itemgetter - Diese beiden Funktionen liefern also bei Aufruf eine Funktion, nicht einen passiven Datenwert.
Nur deshalb können wir einen solchen Aufruf für
key=
beisorted()
gebrauchen. - 4 Ergänzen Sie
import operator
und benutzen Sie einen Aufruf vonoperator.itemgetter
als Funktion, um das dritte Element jedes Tupels als Sortierschlüssel zu benutzen. Geben Sie bei Schritt 3 die so sortierte Liste von Tupeln aus.
Hinweis (nur bei Bedarf): Warum bekomme ich dabei immer eine Fehlermeldung?
Haben Sie daran gedacht, dass das dritte Element den Index 2 hat?
- 5 Nach einer einfachen Faustformel ist "Normalgewicht" die Körpergröße minus 100
(jedenfalls für Männer, aber den Unterschied ignorieren wir hier).
Übergewicht hat also, wer schwerer ist als dieser Wert.
(Negative Werte sind nicht sofort ein Untergewicht, aber auch diese Feinheit ignorieren wir hier.)
Schreiben Sie eine Funktionexcess_weight(mytuple: tuple[int, int, int]) -> int
, die das positive oder negative "Übergewicht" in diesem Sinne berechnet, übergeben Sie sie beikey
und geben Sie bei Schritt 4 die so sortierte Liste von Tupeln aus. Das war eine reine Fingerübung, es ist ganz analog zu Schritt 2. - Aber dieser Ansatz mit einer
key
-Funktion hat Grenzen. Es gibt Sortierkriterien, die sich nicht auf einen einzelnen Datenwert reduzieren lassen, sondern eine Kaskade von Abfragen benötigen. - Beispielsweise dieses: Gesucht ist eine Sortierung, die als Erstes die Gruppen
klein
(bis 172cm) undgroß
(über 172cm) trennt und sodann innerhalb dieser Gruppen nach Alter, dann bei Gleichheit nach Gewicht und erst zuletzt bei Gleichheit wieder nach Größe sortieren soll. - Dafür brauchen wir eine Funktion, die direkt den Vergleich zweier Tupel beantwortet anstatt einen einzigen Sortierschlüssel zu liefern, der alle Kriterien abbildet.
- Auch das geht in Python und sogar relativ einfach, wenn die zu sortierenden Objekte zu einer Klasse gehören, die wir erweitern können. Bei Tupeln ist das erstmal nicht der Fall, also müssen wir die Tupel zu Klassenobjekten "aufbohren".
- 6 Fügen Sie folgendes Ihrem Programm an geeigneter Stelle hinzu:
1 2 3 4 |
|
- Damit sind die Tupel in
data2
gleichwertig zu denen indata
, aber sie gehören nun einer Klasse an, die wir erweitern können. Dieser Kniff (für Tupel, Listen und Dictionaries) ist in Python nicht ungewöhnlich. - Python benutzt für die Vergleiche beim Sortieren, den "kleiner"-Operator. In Python kann man Operatoren überladen, also selber umdefinieren. Dazu haben die Operatoren einen festen Funktionsnamen. Wir diese Funktion als Methode in einer Klasse definiert, ist der Operator für Objekte dieser Klasse überladen
- Der Funktionsname für "kleiner" lautet
__lt__
("less than"). - 7 Schreiben sie nun also in
MyTuple
__lt__(self, other)
im oben beschriebenen Sinn. - Listen haben eine Methode
sort()
, die die Liste am Platze sortiert, also ohne dabei eine Kopie anzulegen wie bei der globalen Funktionsorted()
. - 8 Sortieren Sie damit
data2
am Platze (also ohne Zuweisung). - 9 Geben Sie dann für Schritt 5
data2
aus, das nun die mit__lt__
definierte Sortierung widerspiegeln müsste.
Hinweis (nur bei Bedarf): Muss mein __lt__
wirklich so umständlich aussehen?
Wahrscheinlich nicht. So geht es ganz hübsch:
1 2 3 4 5 6 7 8 9 |
|
- 1 Lassen Sie das fertige Skript einmal laufen.
- Wer noch weitere Techniken für fortgeschrittenes Sortieren kennenlernen möchte, liest hier nach: https://docs.python.org/3/howto/sorting.html
Abgabe
Geben Sie den Quellcode ab, wie er am Ende der Aufgabe vorliegt.
Geben Sie ein Kommandoprotokoll ab, das genau nur die Eingaben und Ausgaben der obigen Kommandos 1, 2, … enthält. Entfernen Sie vor Abgabe eventuelle Fehlversuche und sonstige zusätzliche Kommandos aus dem Protokoll.