Come ordinare un elenco di oggetti basato su un attributo degli oggetti?
Domanda
Ho un elenco di oggetti Python che vorrei ordinare per un attributo degli oggetti stessi. L'elenco è simile a:
>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
<Tag: aes>, <Tag: ajax> ...]
Ogni oggetto ha un conteggio:
>>> ut[1].count
1L
Devo ordinare l'elenco in base al numero di conteggi decrescenti.
Ho visto diversi metodi per questo, ma sto cercando le migliori pratiche in Python.
Soluzione
# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)
# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)
Altre informazioni su ordinamento per chiavi »
Altri suggerimenti
Un modo che può essere più veloce, specialmente se la tua lista ha molti record, è usare operator.attrgetter (" count ")
. Tuttavia, questo potrebbe essere eseguito su una versione pre-operatore di Python, quindi sarebbe bello avere un meccanismo di fallback. Potresti voler fare quanto segue, quindi:
try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda
ut.sort(key=keyfun, reverse=True) # sort in-place
I lettori dovrebbero notare che il metodo key =:
ut.sort(key=lambda x: x.count, reverse=True)
è molte volte più veloce dell'aggiunta di ricchi operatori di confronto agli oggetti. Sono stato sorpreso di leggere questo (pagina 485 di "Python in a Nutshell"). Puoi confermarlo eseguendo dei test su questo piccolo programma:
#!/usr/bin/env python
import random
class C:
def __init__(self,count):
self.count = count
def __cmp__(self,other):
return cmp(self.count,other.count)
longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secs
longList2 = longList[:]
longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs
I miei test, molto minimi, mostrano che il primo ordinamento è più di 10 volte più lento, ma il libro dice che è solo circa 5 volte più lento in generale. Il motivo per cui dicono è dovuto all'algoritmo di ordinamento altamente ottimizzato usato in Python ( timsort ).
Tuttavia, è molto strano che .sort (lambda) sia più veloce del semplice vecchio .sort (). Spero che lo risolvano.
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
Approccio orientato agli oggetti
È buona norma rendere la logica di ordinamento degli oggetti, se applicabile, una proprietà della classe anziché incorporata in ogni istanza in cui è richiesto l'ordinamento.
Ciò garantisce coerenza ed elimina la necessità di codice del boilerplate.
Come minimo, è necessario specificare le operazioni __eq__
e __lt__
affinché funzioni. Quindi usa ordinato (list_of_objects)
.
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __eq__(self, other):
return self.rank == other.rank and self.suit == other.suit
def __lt__(self, other):
return self.rank < other.rank
hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]
hand_order = [c.rank for c in hand] # [10, 2, 12, 13, 14]
hand_sorted = sorted(hand)
hand_sorted_order = [c.rank for c in hand_sorted] # [2, 10, 12, 13, 14]
Sembra molto simile a un elenco di istanze del modello ORM di Django.
Perché non ordinarli su query in questo modo:
ut = Tag.objects.order_by('-count')
Aggiungi ricchi operatori di confronto alla classe di oggetti, quindi usa il metodo sort () dell'elenco.
Vedi confronto dettagliato in python .
Aggiorna : sebbene questo metodo funzioni, penso che la soluzione di Triptych sia più adatta al tuo caso perché molto più semplice.