Jak korzystać z niestandardowej funkcji porównania w Pythonie 3?

98

W Pythonie 2.x mogłem przekazać funkcję niestandardową do funkcji sortowanych i .sort

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

Ponieważ w moim języku w tej kolejności występują spółgłoski

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

Ale w Pythonie 3.x wygląda na to, że nie mogłem przekazać cmpsłowa kluczowego

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

Czy są jakieś alternatywy, czy też powinienem napisać własną posortowaną funkcję?

Uwaga: uprościłem, używając "k", "kh" itp. Rzeczywiste znaki to Unicody i jeszcze bardziej skomplikowane, czasami przed i po spółgłoskach pojawiają się samogłoski, zrobiłem niestandardową funkcję porównywania, więc ta część jest w porządku. Jedynym problemem jest to, że nie mogłem przekazać mojej niestandardowej funkcji porównania do sortowania lub .sort

TY
źródło
próbowałeś właśnie sorted(x)?
SilentGhost
@SilentGhost, Aby się upewnić, po prostu spróbowałem ponownie, oczywiście nie działa, ponieważ mój oryginalny język nie znajduje się na liście ustawień regionalnych obsługiwanych przez systemy operacyjne do sortowania.
YOU
1
Możesz opakować swój cmp jako funkcję kluczową. Wyszukaj w serwisie HowToSorting cmp_to_key.
Frank
tutaj jest coś podobnego stackoverflow.com/questions/49327344/ ...
Eziz Durdyyev

Odpowiedzi:

50

Użyj keyargumentu (i postępuj zgodnie z przepisem, jak przekonwertować starą cmpfunkcję na keyfunkcję).

functoolsma funkcję cmp_to_keywymienioną na docs.python.org/3.6/library/functools.html#functools.cmp_to_key

Tim Pietzcker
źródło
+1, wygląda na to, że przepis daje mi obejście, ale myślę, że stracę trochę wydajności, przekazując wszystkie operatory porównania < > = pośrednikowi, ponieważ moje oryginalne niestandardowe sortowanie jest napisane w C, miało około 1 / 2x prędkość domyślne sortowanie.
YOU
2
(Właśnie obejrzałem Twój profil) Twoja firma blokuje dostęp do Google i StackOverflow? Jak głupi mogą być? Ale jeśli chodzi o twoją odpowiedź: byłbym zainteresowany faktycznym spadkiem wydajności. Potrafisz timeitto?
Tim Pietzcker
4
Zrobiłem kilka testów porównawczych, wygląda na około 4x wolniej niż bezpośrednie przekazanie niestandardowej funkcji porównania C.
YOU
2
A jeśli potrzebuję zarówno funkcji klucza, jak i funkcji cmp? Chcę posortować listę słowników według niestandardowego klucza w każdym słowniku. sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)daje TypeError: 'cmp' jest nieprawidłowym argumentem słowa kluczowego dla tej funkcji, w Pythonie 3.2 :(
bitek
4
functools ma funkcję cmp_to_key w standardowej bibliotece: docs.python.org/3.6/library/functools.html
Martín Fixman
59

Użyj keysłowa kluczowego i functools.cmp_to_key, aby przekształcić funkcję porównania:

sorted(x, key=functools.cmp_to_key(customsort))
aknuds1
źródło
17

Zamiast Customort () potrzebujesz funkcji, która tłumaczy każde słowo na coś, co Python już wie, jak posortować. Na przykład, możesz przetłumaczyć każde słowo na listę liczb, gdzie każda cyfra reprezentuje miejsce, w którym każda litera występuje w Twoim alfabecie. Coś takiego:

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)

Ponieważ twój język zawiera wieloznakowe litery, twoja funkcja custom_key będzie oczywiście musiała być bardziej skomplikowana. To jednak powinno dać ci ogólny pomysł.

Daniel Stutzbach
źródło
Dzięki +1, tak myślę na OIOM-ie. ale ponieważ mój język nie ma separatorów słów i nie ma standardowych reguł latynizacji, myślę, że zajmie to trochę czasu.
YOU
9

Kompletny przykład lambda cmp_to_key w Pythonie3:

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

w porównaniu do zwykłego sortowania obiektów:

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 木匠
źródło
4

Nie wiem, czy to pomoże, ale możesz sprawdzić localemoduł. Wygląda na to, że możesz ustawić ustawienia regionalne na swój język i użyć go locale.strcolldo porównania ciągów znaków przy użyciu reguł sortowania w Twoim języku.

Mark Tolonen
źródło
To prawda w przypadku popularnych języków, ale mój język nie jest w pełni obsługiwany przez systemy operacyjne, ICU i unicode.org, więc to wykluczone, ale +1 za dobrą sugestię.
YOU
-2

keyZamiast tego użyj argumentu. Pobiera funkcję, która przyjmuje przetwarzaną wartość i zwraca pojedynczą wartość, podając klucz do sortowania według.

sorted(x, key=somekeyfunc)
Ignacio Vazquez-Abrams
źródło
3
Klucz akceptuje tylko jedną funkcję parametryczną, cmp ma 2 parametry, mają różne zachowanie. i właśnie przetestowałem, dostałem błąd, ponieważ kluczowe słowo kluczowe przekazało tylko jeden parametr,TypeError: customsort() takes exactly 2 positional arguments (1 given)
YOU