Programmierpraktikum SoSe 2024, Bachelor Informatik, FU Berlin
ProPra2024 > Bibliotheken > Python-Standardbibliothek > sorted_and_sort

Sortieren in Python

Trial

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.

Detailed

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
what = "Tupel von Alter, Gewicht, Körpergröße"
data = [
    (22, 69, 177,), 
    (22, 71, 172,), 
    (48, 72, 174,), 
    (38, 89, 179,), 
    (71, 59, 170,), 
]

print(what)
print(data)
print("Sortiert:")
print(...)  # Schritt 1
print("Sortiert nach Körpergröße 1:")
print(...)  # Schritt 2
print("Sortiert nach Körpergröße 2:")
print(...)  # Schritt 3
print("Sortiert nach Übergewicht:")
print(...)  # Schritt 4
print("Sortiert in Gewichtsgruppen:")
print(...)  # Schritt 5

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 Funktion height(mytuple: tuple[int, int, int]) -> int, die das tut, übergeben Sie sie bei key 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 das k-te Element einer Sequenz zu holen; das ist unser Fall) und operator.attrgetter (um ein Attribut mit dem Namen name zu holen).
    Lesen Sie itemgetter 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= bei sorted() gebrauchen.
  • 4 Ergänzen Sie import operator und benutzen Sie einen Aufruf von operator.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 Funktion excess_weight(mytuple: tuple[int, int, int]) -> int, die das positive oder negative "Übergewicht" in diesem Sinne berechnet, übergeben Sie sie bei key 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) und groß (ü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
class MyTuple(tuple):
    ...

data2 = [MyTuple(t) for t in data]
  • Damit sind die Tupel in data2 gleichwertig zu denen in data, 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 Funktion sorted().
  • 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
    def __lt__(self, other: 'MyTuple') -> bool:
        if not self.is_big and other.is_big:
            return True
        else:
            return tuple(self) < tuple(other)

    @property
    def is_big(self) -> bool:
        return self[2] > 172
Trace
Program

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.