J'ai une liste d'objets Python que je souhaiterais trier en fonction d'un attribut des objets eux-mêmes. La liste ressemble à:
>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
<Tag: aes>, <Tag: ajax> ...]
Chaque objet a un compte:
>>> ut[1].count
1L
Je dois trier la liste par nombre de comptes décroissant.
J'ai vu plusieurs méthodes pour cela, mais je recherche les meilleures pratiques en Python.
# 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)
Plus sur tri par clé "
Une façon qui peut être la plus rapide, en particulier si votre liste contient de nombreux enregistrements, consiste à utiliser operator.attrgetter("count")
. Cependant, cela pourrait fonctionner sur une version pré-opérateur de Python, il serait donc agréable d'avoir un mécanisme de secours. Vous voudrez peut-être faire ce qui suit, alors:
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
Les lecteurs devraient remarquer que la méthode clé =:
ut.sort(key=lambda x: x.count, reverse=True)
est plusieurs fois plus rapide que l'ajout d'opérateurs de comparaison riches aux objets. J'ai été surpris de lire ceci (page 485 de "Python in a Nutshell"). Vous pouvez le confirmer en lançant des tests sur ce petit programme:
#!/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
Mes tests, très minimes, montrent que le premier tri est plus de 10 fois plus lent, mais le livre dit que ce n’est que 5 fois plus lent en général. La raison qu'ils disent est due à l'algorithme de tri hautement optimisé utilisé dans python (timsort).
Pourtant, il est très étrange que .sort (lambda) soit plus rapide que plain old .sort (). J'espère qu'ils règlent ça.
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
approche orientée objet
Il est recommandé de faire de la logique de tri d'objet, le cas échéant, une propriété de la classe plutôt que d'incorporer à chaque instance le classement est obligatoire.
Cela garantit la cohérence et élimine le besoin de code passe-partout.
Au minimum, vous devez spécifier les opérations __eq__
et __lt__
pour que cela fonctionne. Ensuite, utilisez simplement 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]
Cela ressemble beaucoup à une liste de Django instances de modèle ORM.
Pourquoi ne pas les trier sur la requête comme ceci:
ut = Tag.objects.order_by('-count')
Ajoutez des opérateurs de comparaison riches à la classe d’objets, puis utilisez la méthode sort () de la liste.
Voir comparaison riche en python .
Mise à jour : Bien que cette méthode fonctionne, je pense que la solution de Triptych est mieux adaptée à votre cas car beaucoup plus simple.