Como classificar uma lista de objetos com base em um atributo dos objetos?
Pergunta
Eu tenho uma lista de objetos Python que eu gostaria de classificar por um atributo dos próprios objetos. A lista parece com:
>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
<Tag: aes>, <Tag: ajax> ...]
Cada objeto tem uma contagem:
>>> ut[1].count
1L
Eu preciso ordenar a lista por número de contagens decrescentes.
Já vi vários métodos para isso, mas eu estou procurando as melhores práticas em Python.
Solução
# 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)
Mais sobre ordenação por chaves »
Outras dicas
Um caminho que pode ser mais rápido, especialmente se a sua lista tem um monte de discos, é usar operator.attrgetter("count")
. No entanto, isso pode ser executado em uma versão de pré-operador de Python, por isso seria bom ter um mecanismo de retorno. Você pode querer fazer o seguinte, então:
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
Os leitores devem notar que a chave = método:
ut.sort(key=lambda x: x.count, reverse=True)
é muitas vezes mais rápido do que adicionar operadores de comparação ricos para os objetos. Fiquei surpreso ao ler este (página 485 de "Python in a Nutshell"). Você pode confirmar isso executando testes em este pequeno programa:
#!/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
O meu, muito mínimo, os testes mostram o primeiro tipo é mais de 10 vezes mais lento, mas o livro diz que é apenas cerca de 5 vezes mais lento em geral. A razão que eles dizem que é devido ao altamente otimiza tipo algoritmo usado em python ( timsort ).
Ainda assim, é muito estranho que .Sort (lambda) é mais rápido do que simples .Sort de idade (). Espero que corrigir isso.
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
abordagem orientada a objetos
É uma boa prática para fazer objeto de classificação lógica, se for o caso, uma propriedade da classe em vez de incorporados em cada caso, é necessária a ordenação.
Esta consistência garante e elimina a necessidade de código clichê.
No mínimo, você deve especificar as operações __eq__
e __lt__
para que isso funcione. Em seguida, basta usar sorted(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]
Ele se parece muito com uma lista de instâncias do modelo Django ORM.
Por que não tipo-los em consulta como esta:
ut = Tag.objects.order_by('-count')
Adicionar operadores de comparação ricos para a classe de objeto, em seguida, usar método sort () da lista.
Veja comparação rico em python .
Atualizar :. Embora este método iria funcionar, eu acho solução de Tríptico é mais adequado para o seu caso, porque maneira mais simples