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?


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.


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.


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)


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)

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))

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.