¿Cómo usar una función de comparación personalizada en Python 3?

98

En Python 2.x , podría pasar una función personalizada a funciones ordenadas y .sort

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

Porque, en mi idioma, los consonentes vienen con este orden

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

Pero en Python 3.x , parece que no pude pasar la cmppalabra clave

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

¿Hay alguna alternativa o debería escribir mi propia función ordenada también?

Nota: simplifiqué usando "k", "kh", etc. Los caracteres reales son Unicodes e incluso más complicados, a veces hay vocales que vienen antes y después de los consonents, hice una función de comparación personalizada, así que esa parte está bien. El único problema es que no pude pasar mi función de comparación personalizada a sorted o .sort


fuente
has probado solo sorted(x)?
SilentGhost
@SilentGhost, para asegurarme, lo intenté de nuevo, por supuesto que no funciona, porque mi idioma original no está en la lista de configuraciones regionales compatible con Operation Systems para realizar la clasificación.
USTED
1
Puede envolver su cmp como una función clave. Busque en el sitio HowToSorting cmp_to_key.
Frank
aquí hay algo similar stackoverflow.com/questions/49327344/…
Eziz Durdyyev

Respuestas:

50

Utilice el keyargumento (y siga la receta sobre cómo convertir su antigua cmpfunción en una keyfunción).

functoolstiene una función cmp_to_keymencionada en docs.python.org/3.6/library/functools.html#functools.cmp_to_key

Tim Pietzcker
fuente
+1, parece que la receta me da una solución alternativa, pero creo que voy a perder algo de rendimiento al pasar todos los operadores de comparación < > = al intermediario, ya que mi clasificación personalizada original está escrita en C, tenía una velocidad de alrededor de 1 / 2x clasificación predeterminada.
USTED
2
(Solo miró su perfil) ¿Su empresa está bloqueando el acceso a Google y StackOverflow? ¿Qué tan estúpidos pueden llegar a ser? Pero sobre su respuesta: me interesaría la disminución real del rendimiento. ¿Puedes timeithacerlo?
Tim Pietzcker
4
He hecho algunos puntos de referencia, parece alrededor de 4 veces más lento que pasar la función de comparación C personalizada directamente.
USTED
2
¿Qué pasa si necesito una función clave Y una función cmp? Quiero ordenar una lista de diccionarios por una clave personalizada en cada diccionario. sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)da TypeError: 'cmp' es un argumento de palabra clave no válido para esta función, en Python 3.2 :(
bitek
4
functools tiene una función cmp_to_key en la biblioteca estándar: docs.python.org/3.6/library/functools.html
Martín Fixman
59

Utilice la keypalabra clave y functools.cmp_to_key para transformar su función de comparación:

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

En lugar de customsort (), necesita una función que traduzca cada palabra en algo que Python ya sepa cómo ordenar. Por ejemplo, puede traducir cada palabra en una lista de números donde cada número representa el lugar donde aparece cada letra en su alfabeto. Algo como esto:

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)

Dado que su idioma incluye letras de varios caracteres, su función custom_key obviamente tendrá que ser más complicada. Sin embargo, eso debería darte una idea general.

Daniel Stutzbach
fuente
Gracias +1, creo que así es en la UCI. pero como mi idioma no tiene separadores de palabras y no tiene reglas estándar de romanización, creo que llevará tiempo investigarlo.
USTED
9

Un ejemplo completo de python3 cmp_to_key lambda:

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

comparar con la clasificación de objetos comunes:

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 木匠
fuente
4

No sé si esto ayudará, pero puede consultar el localemódulo. Parece que puede establecer la configuración regional en su idioma y usarla locale.strcollpara comparar cadenas usando las reglas de clasificación de su idioma.

Mark Tolonen
fuente
Eso es cierto para los idiomas populares, pero mi idioma no es totalmente compatible con Operation Systems, ICU y unicode.org, por lo que eso está fuera de duda, pero +1 por una buena sugerencia.
USTED
-2

En su lugar, utilice el keyargumento. Toma una función que toma el valor que se está procesando y devuelve un valor único que proporciona la clave que se utilizará para ordenar.

sorted(x, key=somekeyfunc)
Ignacio Vázquez-Abrams
fuente
3
La tecla solo acepta una función de parámetro, cmp tiene 2 parámetros, tienen un comportamiento diferente. y acabo de probar, obtuve un error, debido a que la palabra clave clave solo pasó un parámetro,TypeError: customsort() takes exactly 2 positional arguments (1 given)
USTED