Ordinamento degli elenchi Python personalizzato


93

Stavo rifattorizzando un mio vecchio codice e mi sono imbattuto in questo:

alist.sort(cmp_items)

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

Il codice funziona (e l'ho scritto circa 3 anni fa!) Ma non riesco a trovare questa cosa documentata da nessuna parte nei documenti di Python e tutti usano sorted()per implementare l'ordinamento personalizzato. Qualcuno può spiegare perché funziona?


sorted()e sort()offrono l'ordinamento personalizzato più o meno allo stesso modo, modulo la differenza nella convenzione di chiamata.
Russell Borogove

2
In effetti, ciò che accade è che l'utilizzo di un keyparametro è preferito rispetto al passaggio di una cmpfunzione. (Il successivo non è nemmeno implementato in Python 3)
jsbueno

È un po 'ambiguo, dipende da quali erano gli elementi nell'elenco; il tuo codice richiede che abbiano un attributo foo, altrimenti esplode. Meglio definire un __lt__()metodo personalizzato per la tua classe, quindi sorted()e list.sort()funzionerà immediatamente. (Btw, gli oggetti non devono più essere definiti __cmp__(), solo __lt__(). Vedi questo
smci

Risposte:


60

È documentato qui .

Il metodo sort () accetta argomenti opzionali per controllare i confronti.

cmp specifica una funzione di confronto personalizzata di due argomenti (voci di elenco) che dovrebbe restituire un numero negativo, zero o positivo a seconda che il primo argomento sia considerato minore, uguale o maggiore del secondo argomento: cmp = lambda x, y : cmp (x.lower (), y.lower ()). Il valore predefinito è Nessuno.


Grazie miles82, stavo controllando qui e non sono riuscito a vederlo nella firma del metodo docs.python.org/tutorial/datastructures.html
Lorenzo

Non vedo lo stesso testo nella pagina a cui sei collegato. La documentazione è cambiata. Inoltre, quando provo a usare cmp, ottengo TypeError: 'cmp' is an invalid keyword argument for this function. Che cosa sta succedendo qui?
Ciao Arrivederci

1
@ HelloGoodbye sort () non ha un argomento cmp in Python 3. Questa è una vecchia risposta quando il collegamento ai documenti era per Python 2. Puoi trovare i vecchi documenti qui o leggere di più qui . Se stai usando Python 3, usa invece l' argomento chiave .
miglia 82

E se volessi effettivamente fornire una funzione di confronto? Voglio trattare i numeri in una stringa (di qualsiasi lunghezza, scelti avidamente) come simboli, in modo equivalente a come i singoli caratteri vengono altrimenti trattati. So come ottenerlo banalmente se posso fornire una funzione di confronto, ma non se devo fornire una funzione chiave. Perché è stato cambiato?
Ciao Arrivederci

Immagino che possa ancora essere ottenuto se ogni numero contenuto nella stringa viene codificato utilizzando una codifica che ordina i numeri in modo lessicografico, come la codifica Levenshtein . Ma lo considero più come una soluzione alternativa al fatto che sortnon prende una funzione di confronto come argomento in Python 3, e non come qualcosa che mi piacerebbe davvero fare.
Ciao Arrivederci

104

Come nota a margine, ecco un'alternativa migliore per implementare lo stesso ordinamento:

alist.sort(key=lambda x: x.foo)

Oppure in alternativa:

import operator
alist.sort(key=operator.attrgetter('foo'))

Controlla l' ordinamento come fare , è molto utile.


1
TIL sull'operatore, molto utile.
ffledgling

13

Proprio come questo esempio. Vuoi ordinare questo elenco.

[('c', 2), ('b', 2), ('a', 3)]

produzione:

[('a', 3), ('b', 2), ('c', 2)]

dovresti ordinare le tuple per il secondo elemento, poi il primo:

def letter_cmp(a, b):
    if a[1] > b[1]:
        return -1
    elif a[1] == b[1]:
        if a[0] > b[0]:
            return 1
        else:
            return -1
    else:
        return 1

Finalmente:

appena sort(letter_cmp)


4
Come fa a sapere quale elenco ordinare?
Cameron Monks

2
@CameronMonks yourList.sort (letter_cmp)
Minyc510

7

Questo non funziona in Python 3.

Puoi usare functools cmp_to_key per far funzionare le funzioni di confronto vecchio stile.

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

cmp_items_py3 = cmp_to_key(cmp_items)

alist.sort(cmp_items_py3)

1

In python3 puoi scrivere:

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

alist = sorted(alist, key=cmp_to_key(cmp_items))

0

Anche meglio:

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]

sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

Tratto da: https://docs.python.org/3/howto/sorting.html

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.