Wie verwende ich eine benutzerdefinierte Vergleichsfunktion in Python 3?

98

In Python 2.x konnte ich benutzerdefinierte Funktionen an sortierte und sortierte Funktionen übergeben

>>> x=['kar','htar','har','ar']
>>>
>>> sorted(x)
['ar', 'har', 'htar', 'kar']
>>> 
>>> sorted(x,cmp=customsort)
['kar', 'htar', 'har', 'ar']

Weil in meiner Sprache Konsonanten mit dieser Reihenfolge kommen

"k","kh",....,"ht",..."h",...,"a"

In Python 3.x konnte ich jedoch kein cmpSchlüsselwort übergeben

>>> sorted(x,cmp=customsort)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'cmp' is an invalid keyword argument for this function

Gibt es Alternativen oder sollte ich auch meine eigene sortierte Funktion schreiben?

Hinweis: Ich habe durch die Verwendung von "k", "kh" usw. vereinfacht. Tatsächliche Zeichen sind Unicodes und noch komplizierter. Manchmal gibt es Vokale vor und nach Konsonenten. Ich habe eine benutzerdefinierte Vergleichsfunktion durchgeführt. Dieser Teil ist also in Ordnung. Das einzige Problem ist, dass ich meine benutzerdefinierte Vergleichsfunktion nicht an sortiert oder .sort übergeben konnte

SIE
quelle
haben Sie versucht gerade sorted(x)?
SilentGhost
@SilentGhost, Um sicherzugehen, habe ich es gerade noch einmal versucht. Natürlich funktioniert es nicht, da meine Originalsprache nicht in der Gebietsschemaliste enthalten ist, die von Operations Systems für die Sortierung unterstützt wird.
SIE
1
Sie können Ihren CMP als Schlüsselfunktion umschließen. Durchsuchen Sie die HowToSorting-Site nach cmp_to_key.
Frank
Hier ist etwas ähnliches stackoverflow.com/questions/49327344/…
Eziz Durdyyev

Antworten:

50

Verwenden Sie das keyArgument (und befolgen Sie das Rezept zum Konvertieren Ihrer alten cmpFunktion in eine keyFunktion).

functoolshat eine Funktion, cmp_to_keydie unter docs.python.org/3.6/library/functools.html#functools.cmp_to_key erwähnt wird

Tim Pietzcker
quelle
+1, sieht so aus, als würde mir das Rezept eine Problemumgehung geben, aber ich denke, ich werde etwas an Leistung verlieren, wenn ich alle Vergleichsoperatoren < > = an Middle Man übergebe, da meine ursprüngliche benutzerdefinierte Sortierung in C geschrieben ist und eine Geschwindigkeit von ungefähr 1 / 2x hat Standardsortierung.
SIE
2
(Sie haben sich gerade Ihr Profil angesehen.) Ihr Unternehmen blockiert den Zugriff auf Google und StackOverflow. Wie dumm können sie werden? Aber zu Ihrer Antwort: Ich würde mich für den tatsächlichen Leistungsabfall interessieren. Kannst du timeites?
Tim Pietzcker
4
Ich habe einige Benchmarks durchgeführt und sehe ungefähr viermal langsamer aus, als die benutzerdefinierte C-Vergleichsfunktion direkt zu übergeben.
SIE
2
Was ist, wenn ich sowohl eine Tastenfunktion als auch eine CMP-Funktion benötige? Ich möchte eine Liste von Wörterbüchern nach einem benutzerdefinierten Schlüssel in jedem Wörterbuch sortieren. sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)gibt TypeError an: 'cmp' ist ein ungültiges Schlüsselwortargument für diese Funktion in Python 3.2 :(
Bitek
4
functools hat eine cmp_to_key-Funktion in der Standardbibliothek: docs.python.org/3.6/library/functools.html
Martín Fixman
59

Verwenden Sie das keySchlüsselwort und functools.cmp_to_key, um Ihre Vergleichsfunktion zu transformieren:

sorted(x, key=functools.cmp_to_key(customsort))
aknuds1
quelle
17

Anstelle eines Zolls () benötigen Sie eine Funktion, die jedes Wort in etwas übersetzt, das Python bereits sortieren kann. Sie könnten beispielsweise jedes Wort in eine Liste von Zahlen übersetzen, wobei jede Zahl darstellt, wo jeder Buchstabe in Ihrem Alphabet vorkommt. Etwas wie das:

my_alphabet = ['a', 'b', 'c']

def custom_key(word):
   numbers = []
   for letter in word:
      numbers.append(my_alphabet.index(letter))
   return numbers

x=['cbaba', 'ababa', 'bbaa']
x.sort(key=custom_key)

Da Ihre Sprache mehrstellige Buchstaben enthält, muss Ihre custom_key-Funktion natürlich komplizierter sein. Das sollte Ihnen jedoch die allgemeine Idee geben.

Daniel Stutzbach
quelle
Danke +1, so denke ich auf der Intensivstation. Aber da meine Sprache keine Worttrenner und keine Standardregeln für die Romanisierung hat, wird es meiner Meinung nach einige Zeit dauern, nachzuforschen.
SIE
9

Ein vollständiges python3 cmp_to_key Lambda-Beispiel:

from functools import cmp_to_key

nums = [28, 50, 17, 12, 121]
nums.sort(key=cmp_to_key(lambda x, y: 1 if str(x)+str(y) < str(y)+str(x) else -1))

Vergleiche mit der allgemeinen Objektsortierung:

class NumStr:
    def __init__(self, v):
        self.v = v
    def __lt__(self, other):
        return self.v + other.v < other.v + self.v


A = [NumStr("12"), NumStr("121")]
A.sort()
print(A[0].v, A[1].v)

A = [obj.v for obj in A]
print(A)
Charlie 木匠
quelle
4

Ich weiß nicht, ob dies helfen wird, aber Sie können das localeModul überprüfen . Es sieht so aus, als könnten Sie das Gebietsschema auf Ihre Sprache einstellen und locale.strcollZeichenfolgen anhand der Sortierregeln Ihrer Sprache vergleichen.

Mark Tolonen
quelle
Das gilt für beliebte Sprachen, aber meine Sprache wird von Operation Systems, ICU und unicode.org nicht vollständig unterstützt. Das kommt also nicht in Frage, aber +1 für gute Vorschläge.
SIE
-2

Verwenden Sie keystattdessen das Argument. Es wird eine Funktion verwendet, die den zu verarbeitenden Wert übernimmt und einen einzelnen Wert zurückgibt, der den Schlüssel zum Sortieren angibt.

sorted(x, key=somekeyfunc)
Ignacio Vazquez-Abrams
quelle
3
Schlüssel akzeptieren nur eine Parameterfunktion, cmp haben 2 Parameter, sie sind unterschiedlich verhalten. und ich gerade getestet, erhielt Fehler, weil der Schlüssel Schlüsselwort nur ein Parameter übergeben,TypeError: customsort() takes exactly 2 positional arguments (1 given)
YOU