Unterschied zwischen sort und sorted()

Wie sortiert man eine Liste?

Man wird entweder Pythons eingebaute sort()-Funktion oder die sort()-Methode verwenden.
Aber was ist der Unterschied zwischen sort() und sorted()? Welche Funktion sollte man bevorzugen und wann?
In diesem Artikel werden die Fragen beantwortet.

Syntax

Die Syntax für sort() und sorted() ist im Folgenden dargestellt. Es ist zu beachten, dass ‚/‘ das Ende von Positionsparametern und * den Beginn von Schlüsselwortparametern angibt. 

sorted(iterable, /, *, key=None, reverse=False)
sort(*, key=None, reverse=False)

wobei

iterable: iterable wie Liste, Tupel, Set, Dictionary usw.
key: benutzerdefinierte Funktion zum Anpassen der Sortierreihenfolge.
reverse: wenn auf True gesetzt, wird in absteigender Reihenfolge sortiert.

Sortierung Liste

Der Hauptunterschied zwischen sort() und sorted() besteht darin, dass die Funktion sorted() eine beliebige Iterable (Liste, Tupel, Set, Dictionary) nimmt und ein neues sortiertes Objekt zurückgibt, ohne das Original zu verändern. Andererseits führt sort() eine Sortierung an Ort und Stelle durch, d.h. es wird keine neue Liste erstellt, sondern die gegebene Liste selbst in der gewünschten Reihenfolge (auf- oder absteigend) aktualisiert.

Im folgenden einfachen Beispiel kann man sehen, dass sorted() ein neues Objekt zurückgibt, während sort() einen Sortiervorgang an Ort und Stelle durchführt. Außerdem erfolgt die Sortierung standardmäßig in aufsteigender Reihenfolge.

sort()

>>> mylist = [5, 4, 3, 2, 1]
>>> mylist.sort()
>>> mylist
[1, 2, 3, 4, 5]

sorted()

>>> mylist = [5, 4, 3, 2, 1]
>>> sorted(mylist)
[1, 2, 3, 4, 5]
>>> mylist
[5, 4, 3, 2, 1]

Man kann nicht nur Listen, sondern auch beliebige iterierbare Tupel, Listen und Dictionries mit der Funktion sorted() sortieren.

Sortierung Dictionary

Wenn man versucht, die Dictionary-Objekte zu sortieren, erfolgt die Sortierung anhand der Schlüssel.

>>> my_dict = {3:"Javascript", 2:"Java", 1: "Python"}
>>> sorted(my_dict)
[1, 2, 3]

Sortierung Set

>>> my_set = set(["Python", "Java", "Javascript"])
>>> sorted(my_set)
['Java', 'Javascript', 'Python']

Hinweis
Die Funktion sort() gibt das Ergebnis immer als Liste zurück. Es ist uz beachten, dass die sort()-Methode nur für die Liste verwendet wird. Für Sets und Dictionaries gibt es keine sort()-Methode, da es sich um ungeordnete Sammlungen von Elementen handelt. Sie an Ort und Stelle zu sortieren, ergibt keinen Sinn.

Wie man in der obigen Syntax sehen kann, haben sowohl sort() als auch sorted() zwei Argumente, die nur für Schlüsselwörter gelten – key und reverse. Hierzu werden wir die Beispiele anhand der sort()-Funktion betrachten. Die gleichen Beispiele gelten aber auch für die Methode sort(), allerdings nur für Listen.

Sortierung mit Key-Parameter

Der Key-Parameter nimmt eine Funktion oder eine aufrufbare Variable als Wert an, die dann auf jedes Element vor der Sortierung angewendet wird. Schauen wir uns einige Beispiele an, wie dieser Schlüsselparameter verwendet werden kann.

Beispiel 1

Wenn man versucht, ein Dictionary-Objekt zu sortieren, wird es standardmäßig nach den Dictionary-Schlüsseln sortiert, wie man im vorherigen Beispiel gesehen hat. Wenn man Dictionary-Werte für die Sortierung verwenden möchte, kann man den Parameter key verwenden, wie im folgenden Beispiel gezeigt.

>>> my_dict = {"Javascript":2, "Python":1, "C":2}
>>> sorted(my_dict, key=lambda x: my_dict[x])
['Python', 'Javascript', 'C']

Beispiel 2

Nehmen wir an, man hat eine Liste von Zeichenketten und möchte nach der Länge der einzelnen Zeichenketten sortieren. Um dies zu erreichen, muss man die Funktion len() auf jedes Element der Liste anwenden. Die Funktion key=lambda x:len(x) tut genau das. Sie berechnet die Länge jedes Elements vor dem Sortiervorgang und sortiert auf der Grundlage der Länge der Zeichenketten.

>>> my_list = ['Python', 'C', 'C++', 'Java', "Javascript"]
>>> sorted(my_list, key=lambda x: len(x))
['C', 'C++', 'Java', 'Python', 'Javascript']

Beispiel 3

Hier ein weiteres Beispiel. Angenommen, man hat eine Klasse namens Student mit den Attributen name, grade und marks. Aus der Liste der Schüler-Objekte s1, s2 und s3 kann man entweder nach Name, nach Note oder nach Bewertung sortieren.

class Student:

    def __init__(self, name, grade, marks):
        self.name = name
        self.grade = grade
        self.marks = marks

    def __repr__(self):
        return repr((self.name, self.grade, self.marks))

s1 = Student(‚Chetan‘, ‚C‘, 50)
s2 = Student(‚Swathi‘, ‚A‘, 90)
s3 = Student(‚Megha‘, ‚B‘, 70)

student_objects = [s1, s2, s3]

Sortierung nach Bewertung

Der folgende Code sortiert nach den Bewertungen. Hier übergeben wir das benannte Attribut marks des Objekts, d.h. st.marks, an den Schlüsselparameter. So dass die Sortierung auf der Grundlage der Bewertungen erfolgt.

>>> sorted(student_objects, key=lambda st:st.marks)
[('Megha', 'C', 50), ('Swathi', 'B', 70), ('Chetan', 'A', 90)]

Sortierung anhand der Note

Der folgende Code sortiert anhand der Note anstelle von Bewertungen. Hier übergeben wir das benannte Attribut grade des Objekts, d.h. st.grade, als Schlüsselparameter. Nun erfolgt die Sortierung anhand der Note.

>>> sorted(student_objects, key=lambda st:st.grade)
[('Swathi', 'A', 90), ('Megha', 'B', 70), ('Chetan', 'C', 50)]

Beispiel 4

Pythons Operator-Modul bietet zwei Methoden itemgetter() und attrgetter(), die auch im Schlüsselparameter verwendet werden können. 

Beispiel itemgetter: Der itemgetter holt das Element aus seinem Operanden. Im folgenden Beispiel holt itemgetter(0) den Namen, itemgetter(1) die Note und itemgetter(2) die Bewertung aus student_tuples. Die Sortierung erfolgt dann auf der Grundlage des im Schlüsselparameter verwendeten Parameters.

>>> student_tuples = [('Chetan', 'C', 50),
                     ('Swathi', 'A', 90),
                      ('Megha', 'B', 70)]

# sorts based on name
>>> sorted(student_tuples, key=itemgetter(0))
[('Chetan', 'C', 50), ('Megha', 'B', 70), ('Swathi', 'A', 90)]

# sorts based on grade
>>> sorted(student_tuples, key=itemgetter(1))
[('Swathi', 'A', 90), ('Megha', 'B', 70), ('Chetan', 'C', 50)]

# sorts based on marks
>>> sorted(student_tuples, key=itemgetter(2))
[('Chetan', 'C', 50), ('Megha', 'B', 70), ('Swathi', 'A', 90)]

Beispiel attrgetter: Wie der Name schon sagt, holt er das Attribut aus seinem Operanden. Im folgenden Beispiel holt attrgetter(’name‘) den Namen, attrgetter(‚grade‘) die Note und attrgetter(‚marks‘) die Bewertung. Dann erfolgt die Sortierung auf der Grundlage dessen, was man dem Schlüsselparameter übergibt.

>>> student_objects = [s1, s2, s3]

# sorts by name
>>> sorted(student_objects, key=attrgetter('name'))
[('Chetan', 'C', 50), ('Megha', 'B', 70), ('Swathi', 'A', 90)]

# sorts by grade
>>> sorted(student_objects, key=attrgetter('grade'))
[('Swathi', 'A', 90), ('Megha', 'B', 70), ('Chetan', 'C', 50)]

# sorts by marks
>>> sorted(student_objects, key=attrgetter('marks'))
[('Chetan', 'C', 50), ('Megha', 'B', 70), ('Swathi', 'A', 90)]

Reverse-Parameter

Der Reverse-Parameter ist recht einfach zu verstehen. Standardmäßig erfolgt die Sortierung in aufsteigender Reihenfolge (reverse=False). Wenn man in absteigender Reihenfolge sortieren möchte, muss man reverse=True setzen.

>>> mylist = [5, 3, 1, 2, 4]
>>> sorted(mylist, reverse=False)
[1, 2, 3, 4, 5]
>>> sorted(mylist, reverse=True)
[5, 4, 3, 2, 1]

Natürliche Sortierung für eine benutzerdefinierte Klasse

Wenn man versucht, die zuvor erstellten Student-Objekte [s1, s2, s3] durch Übergabe des Key-Parameters zu sortieren, tritt ein TypeError auf, wie unten dargestellt. Denn es ist keine natürliche Sortierung für die Student-Klasse implementiert. Man kann die natürliche Reihenfolge für benutzerdefinierte Klassen mit __lt__() oder __gt__() Methoden implementieren.

>>> student_objects = [s1, s2, s3]
>>> sorted(student_objects)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-141-a6e87c8cedd3> in <module>
----> 1 sorted(student_objects)
TypeError: '<' not supported between instances of 'Student' and 'Student'

In der modifizierten Version der Klasse Student wurde die Methode __lt__() hinzugefügt. Wie man erkennen kann, wird die Methode __lt__() für das Attribut marks der Klasse implementiert. Wenn man also mit diesem Code versuchet, die Schülerobjekte mit sorted(student_objects) zu sortieren, werden sie auf der Grundlage der Bewertungen sortiert, und man erhält keinen TypeError mehr.

class Student:

    def __init__(self, name, grade, marks):
        self.name = name
        self.grade = grade
        self.marks = marks

    def __repr__(self):
        return repr((self.name, self.grade, self.marks))

   def __lt__(self, other):
        return self.marks < other.marks

s1 = Student('Chetan', 'C', 50)
s2 = Student('Swathi', 'A', 90)
s3 = Student('Megha', 'B', 70)

student_objects = [s1, s2, s3]

sorted(student_objects)

Ausgabe:

[('Chetan', 'C', 50), ('Megha', 'B', 70), ('Swathi', 'A', 90)]

Was ist schneller: sort() oder sorted()

Die Funktion sorted() erstellt während des Sortiervorgangs eine Kopie des Objekts. Dies ist ein zusätzlicher Overhead im Vergleich zu einer sort()-Operation, die eine In-Place-Operation ist. Daher läuft sort() schneller als sorted().

# Speed using sorted() function
>>> nums = [random.random() for i in range(10_000_000)]
>>> timeit.timeit(stmt="sorted(nums)", globals=globals(), number=1)
9.425759500001732

# Speed using sort() method
>>> nums = [random.random() for i in range(10_000_000)]
>>> timeit.timeit(stmt="nums.sort()", globals=globals(), number=1)
8.331430700000055

Wann sollte man sort() und sorted() verwenden?

  • Wenn man die Liste ändern muss, sollte man sort() verwenden, ansonsten sorted(). 
  • Wenn mane eine schnellere Operation wünscht, sollte man sort() verwenden, da es schneller als sorted() ist.