Come utilizzare una funzione di confronto personalizzata in Python 3?


98

In Python 2.x , potevo passare la funzione personalizzata alle funzioni ordinate e .sort

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

Perché, nella mia lingua, le consonanze vengono fornite con quest'ordine

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

Ma in Python 3.x , sembra che non sia riuscito a passare la cmpparola chiave

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

Ci sono alternative o dovrei scrivere anche la mia funzione ordinata?

Nota: ho semplificato usando "k", "kh", ecc. I caratteri effettivi sono Unicode e anche più complicati, a volte ci sono vocali che vengono prima e dopo le consonanze, ho fatto una funzione di confronto personalizzata, quindi quella parte è ok. L'unico problema è che non sono riuscito a passare la mia funzione di confronto personalizzato a ordinato o .sort


hai provato solo sorted(x)?
SilentGhost

@SilentGhost, Per essere sicuro, ho solo provato di nuovo, Ovviamente non funziona, perché la mia lingua originale non è nell'elenco delle impostazioni locali supportate da Operation Systems per eseguire l'ordinamento.
YOU

1
Puoi avvolgere il tuo cmp come una funzione chiave. Cerca nel sito HowToSorting cmp_to_key.
Frank il

Risposte:


50

Usa l' keyargomento (e segui la ricetta su come convertire la tua vecchia cmpfunzione in una keyfunzione).

functoolsha una funzione cmp_to_keymenzionata in docs.python.org/3.6/library/functools.html#functools.cmp_to_key


+1, sembra che la ricetta mi dia una soluzione alternativa, ma penso che perderò alcune prestazioni passando tutti gli operatori di confronto < > = all'intermediario, poiché il mio ordinamento personalizzato originale è scritto in C, aveva una velocità di circa 1 / 2x di ordinamento predefinito.
YOU

2
(Ho appena guardato il tuo profilo) La tua azienda sta bloccando l'accesso a Google e StackOverflow? Quanto possono diventare stupidi? Ma riguardo la tua risposta: sarei interessato all'effettivo calo delle prestazioni. Lo puoi timeit?
Tim Pietzcker

4
Ho fatto alcuni benchmark, sembra circa 4 volte più lento rispetto al passaggio diretto della funzione di confronto C personalizzata.
YOU

2
E se avessi bisogno sia di una funzione chiave che di una funzione cmp? Voglio ordinare un elenco di dizionari in base a una chiave personalizzata in ogni dizionario. sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)restituisce TypeError: "cmp" è un argomento di parola chiave non valido per questa funzione, in Python 3.2 :(
bitek

4
functools ha una funzione cmp_to_key nella libreria standard: docs.python.org/3.6/library/functools.html
Martín Fixman,


17

Invece di customort (), hai bisogno di una funzione che traduca ogni parola in qualcosa che Python sa già come ordinare. Ad esempio, puoi tradurre ogni parola in un elenco di numeri in cui ogni numero rappresenta il punto in cui ogni lettera si trova nel tuo alfabeto. Qualcosa come questo:

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)

Poiché la tua lingua include lettere multi-carattere, la tua funzione custom_key dovrà ovviamente essere più complicata. Questo dovrebbe darti un'idea generale però.


Grazie +1, questo è il modo in cui penso. ma poiché la mia lingua non ha separatori di parole e non ha regole standard di romanizzazione, ci vorrà del tempo per la ricerca, credo.
YOU

9

Un esempio completo di lambda cmp_to_key python3:

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

confronta con l'ordinamento di oggetti comuni:

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)

4

Non so se questo aiuterà, ma puoi controllare il localemodulo. Sembra che tu possa impostare la locale sulla tua lingua e usarla locale.strcollper confrontare le stringhe usando le regole di ordinamento della tua lingua.


Questo è vero per le lingue popolari, ma la mia lingua non è completamente supportata da Operation Systems, ICU e unicode.org, quindi è fuori discussione, ma +1 per un buon suggerimento.
YOU

-2

Usa keyinvece l' argomento. Prende una funzione che prende il valore in elaborazione e restituisce un singolo valore fornendo la chiave da utilizzare per ordinare.

sorted(x, key=somekeyfunc)

3
tasto accetta solo una funzione parametro, cmp ha 2 parametri, hanno un comportamento diverso. e ho appena testato, ho ricevuto un errore, a causa della parola chiave chiave passa solo un parametro,TypeError: customsort() takes exactly 2 positional arguments (1 given)
TU
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.