Benutzerdefinierte Python-Listensortierung

92

Ich habe einen alten Code von mir überarbeitet und bin auf Folgendes gestoßen:

alist.sort(cmp_items)

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

Der Code funktioniert (und ich habe ihn vor 3 Jahren geschrieben!), Aber ich kann dieses Dokument nirgendwo in den Python-Dokumenten dokumentieren und jeder verwendet es sorted(), um eine benutzerdefinierte Sortierung zu implementieren. Kann jemand erklären, warum das funktioniert?

Lorenzo
quelle
sorted()und sort()bieten benutzerdefinierte Sortierung auf die gleiche Weise, modulo den Unterschied in der Aufrufkonvention.
Russell Borogove
2
In der Tat wird die Verwendung eines keyParameters der Übergabe einer cmpFunktion vorgezogen . (
Letzteres
Es ist mehrdeutig und hängt davon ab, welche Elemente in der Liste enthalten sind. Ihr Code erfordert, dass sie ein Attribut haben foo, sonst explodiert es. Es ist besser, eine benutzerdefinierte __lt__()Methode für Ihre Klasse zu definieren , sorted()und list.sort()sie funktioniert sofort. (Übrigens müssen Objekte nicht mehr definiert werden __cmp__(), nur __lt__(). Siehe dies
smci

Antworten:

60

Es ist hier dokumentiert .

Die sort () -Methode verwendet optionale Argumente zur Steuerung der Vergleiche.

cmp gibt eine benutzerdefinierte Vergleichsfunktion von zwei Argumenten (Listenelementen) an, die eine negative, null oder positive Zahl zurückgeben soll, je nachdem, ob das erste Argument als kleiner, gleich oder größer als das zweite Argument angesehen wird: cmp = Lambda x, y : cmp (x.lower (), y.lower ()). Der Standardwert ist Keine.

Meilen82
quelle
Danke Meilen82 Ich habe hier nachgesehen und konnte es nicht in der Methodensignatur docs.python.org/tutorial/datastructures.html sehen
Lorenzo
Ich sehe nicht denselben Text auf der Seite, auf die Sie verlinkt haben. Hat sich die Dokumentation geändert? Außerdem cmpbekomme ich , wenn ich versuche zu benutzen TypeError: 'cmp' is an invalid keyword argument for this function. Was geht hier vor sich?
HelloGoodbye
1
@HelloGoodbye sort () hat keinen cmp Argument in Python 3. Dies ist eine alte Antwort , wenn der Link docs wurde für Python 2. Sie die alten Dokumente finden hier oder lesen Sie mehr darüber hier . Wenn Sie Python 3 verwenden, verwenden Sie stattdessen das Schlüsselargument .
Meilen82
Und was ist, wenn Sie tatsächlich eine Vergleichsfunktion bereitstellen möchten? Ich möchte Zahlen in einer Zeichenfolge (beliebiger Länge, gierig ausgewählt) als Symbole behandeln, entsprechend der Art und Weise, wie einzelne Zeichen ansonsten behandelt werden. Ich weiß, wie ich das trivial erreichen kann, wenn ich eine Vergleichsfunktion bereitstellen darf, aber nicht, wenn ich eine Schlüsselfunktion bereitstellen muss. Warum wurde das geändert?
HelloGoodbye
Ich denke, es kann immer noch erreicht werden, wenn jede in der Zeichenfolge enthaltene Nummer mit einer Codierung codiert wird, die die Nummern lexikografisch ordnet, wie z. B. Levenshtein-Codierung . Ich betrachte dies jedoch eher als Problemumgehung für die Tatsache, dass sorteine Vergleichsfunktion in Python 3 nicht als Argument verwendet wird, und nicht als etwas, das ich eigentlich gerne tun würde.
HelloGoodbye
102

Als Randnotiz ist hier eine bessere Alternative, um dieselbe Sortierung zu implementieren:

alist.sort(key=lambda x: x.foo)

Oder alternativ:

import operator
alist.sort(key=operator.attrgetter('foo'))

Schauen Sie sich die Sortieranleitung an , sie ist sehr nützlich.

Andrew Clark
quelle
1
Bis zum Operator, sehr nützlich.
Ffledgling
13

Genau wie in diesem Beispiel. Sie möchten diese Liste sortieren.

[('c', 2), ('b', 2), ('a', 3)]

Ausgabe:

[('a', 3), ('b', 2), ('c', 2)]

Sie sollten die Tupel nach dem zweiten und dann nach dem ersten Element sortieren:

def letter_cmp(a, b):
    if a[1] > b[1]:
        return -1
    elif a[1] == b[1]:
        if a[0] > b[0]:
            return 1
        else:
            return -1
    else:
        return 1

Schließlich:

gerade sort(letter_cmp)

RryLee
quelle
4
Woher weiß es, welche Liste zu sortieren ist?
Cameron Monks
1
@CameronMonks yourList.sort (letter_cmp)
Minyc510
7

Dies funktioniert in Python 3 nicht.

Sie können jedoch functools cmp_to_key verwenden, damit Vergleichsfunktionen alten Stils funktionieren.

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

cmp_items_py3 = cmp_to_key(cmp_items)

alist.sort(cmp_items_py3)
Die Unfun Cat
quelle
1

In Python3 können Sie schreiben:

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

alist = sorted(alist, key=cmp_to_key(cmp_items))
mece1390
quelle
0

Noch besser:

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]

sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

Entnommen aus: https://docs.python.org/3/howto/sorting.html

Steven
quelle