Comment utiliser une fonction de comparaison personnalisée dans Python 3?

98

Dans Python 2.x , je pourrais passer une fonction personnalisée aux fonctions triées et .sort

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

Parce que, dans Ma langue, les consonnes sont accompagnées de cet ordre

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

Mais dans Python 3.x , il semble que je n'ai pas pu passer le cmpmot - clé

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

Existe-t-il des alternatives ou devrais-je aussi écrire ma propre fonction triée?

Remarque: j'ai simplifié en utilisant "k", "kh", etc. Les caractères réels sont des Unicodes et encore plus compliqués, parfois il y a des voyelles qui viennent avant et après les consonnes, j'ai fait une fonction de comparaison personnalisée, donc cette partie est ok. Le seul problème est que je n'ai pas pu passer ma fonction de comparaison personnalisée à trié ou .sort

TU
la source
avez-vous essayé juste sorted(x)?
SilentGhost
@SilentGhost, Pour être sûr, je viens de réessayer, bien sûr ne fonctionne pas, car ma langue d'origine n'est pas dans la liste de paramètres régionaux prise en charge par Operation Systems pour faire le tri.
VOUS
1
Vous pouvez envelopper votre cmp comme une fonction clé. Recherchez cmp_to_key sur le site HowToSorting.
Frank
voici quelque chose de similaire stackoverflow.com/questions/49327344/…
Eziz Durdyyev

Réponses:

50

Utilisez l' keyargument (et suivez la recette pour convertir votre ancienne cmpfonction en keyfonction).

functoolsa une fonction cmp_to_keymentionnée sur docs.python.org/3.6/library/functools.html#functools.cmp_to_key

Tim Pietzcker
la source
+1, on dirait que la recette me donne une solution de contournement, mais je pense que je vais perdre des performances en passant tous les opérateurs de comparaison < > = à l'intermédiaire, puisque mon tri personnalisé d'origine est écrit en C, il avait une vitesse d'environ 1 / 2x de tri par défaut.
VOUS
2
(Je viens de regarder votre profil) Votre entreprise bloque l'accès à Google et à StackOverflow? Comment peuvent-ils devenir stupides? Mais à propos de votre réponse: je serais intéressé par la diminution réelle des performances. Le pouvez-vous timeit?
Tim Pietzcker
4
J'ai fait quelques benchmarks, cela semble environ 4x plus lent que de passer directement la fonction de comparaison C personnalisée.
VOUS
2
Que faire si j'ai besoin à la fois d'une fonction clé ET d'une fonction cmp? Je souhaite trier une liste de dictionnaires par une clé personnalisée dans chaque dictionnaire. sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)donne TypeError: 'cmp' est un argument mot-clé non valide pour cette fonction, en Python 3.2 :(
bitek
4
functools a une fonction cmp_to_key dans la bibliothèque standard: docs.python.org/3.6/library/functools.html
Martín Fixman
59

Utilisez le keymot - clé et functools.cmp_to_key pour transformer votre fonction de comparaison:

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

Au lieu d'un customsort (), vous avez besoin d'une fonction qui traduit chaque mot en quelque chose que Python sait déjà trier. Par exemple, vous pouvez traduire chaque mot en une liste de nombres où chaque chiffre représente l'emplacement de chaque lettre dans votre alphabet. Quelque chose comme ça:

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)

Puisque votre langue inclut des lettres à plusieurs caractères, votre fonction custom_key devra évidemment être plus compliquée. Cela devrait cependant vous donner une idée générale.

Daniel Stutzbach
la source
Merci +1, c'est comme ça que je pense ICU. mais comme ma langue n'a pas de séparateurs de mots et n'a pas de règles standard de romanisation, il faudra du temps pour faire des recherches, je pense.
VOUS
9

Un exemple lambda complet de python3 cmp_to_key:

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

comparer au tri d'objets commun:

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 木匠
la source
4

Je ne sais pas si cela aidera, mais vous pouvez consulter le localemodule. Il semble que vous puissiez définir les paramètres régionaux sur votre langue et l'utiliser locale.strcollpour comparer des chaînes en utilisant les règles de tri de votre langue.

Mark Tolonen
la source
C'est vrai pour les langues populaires, mais ma langue n'est pas entièrement prise en charge par Operation Systems, ICU et unicode.org, donc c'est hors de question, mais +1 pour une bonne suggestion.
VOUS
-2

Utilisez keyplutôt l' argument. Il prend une fonction qui prend la valeur en cours de traitement et renvoie une valeur unique donnant la clé à utiliser pour trier.

sorted(x, key=somekeyfunc)
Ignacio Vazquez-Abrams
la source
3
la touche n'accepte qu'une fonction de paramètre, cmp a 2 paramètres, ce sont des comportements différents. et je viens de tester, j'ai eu une erreur, à cause du mot-clé clé, ne transmettez qu'un seul paramètre,TypeError: customsort() takes exactly 2 positional arguments (1 given)
VOUS