Question Comment trier une liste d'objets en fonction d'un attribut des objets?


J'ai une liste d'objets Python que j'aimerais trier par 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

J'ai besoin de trier la liste par le nombre de comptes décroissants.

J'ai vu plusieurs méthodes pour cela, mais je suis à la recherche de meilleures pratiques en Python.


533
2017-12-31 16:41


origine


Réponses:


# 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és » 


914
2017-12-31 16:42



Un moyen qui peut être le plus rapide, surtout si votre liste a beaucoup d'enregistrements, est d'utiliser operator.attrgetter("count"). Cependant, cela pourrait fonctionner sur une version pré-opérateur de Python, donc ce serait bien d'avoir un mécanisme de repli. Vous pourriez vouloir 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

60
2017-12-31 17:48



Les lecteurs devraient remarquer que la clé = méthode:

ut.sort(key=lambda x: x.count, reverse=True)

est plusieurs fois plus rapide que d'ajouter des 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 exécutant 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 qu'il est seulement environ 5 fois plus lent en général. La raison qu'ils disent est due à l'algorithme de tri très optimisé utilisé en python (timsort).

Pourtant, c'est très étrange que .sort (lambda) est plus rapide que l'ancienne .sort (). J'espère qu'ils règlent ça.


52
2018-06-12 19:54



from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)

26
2017-12-31 19:00



Cela ressemble beaucoup à une liste d'instances de modèle ORM de Django.

Pourquoi ne pas les trier sur une requête comme celle-ci:

ut = Tag.objects.order_by('-count')

13
2017-12-31 17:10



Approche orientée objet

C'est une bonne pratique de faire de la logique de tri d'objets, le cas échéant, une propriété de la classe plutôt que d'être incorporée dans chaque cas où la commande est requise.

Cela garantit la cohérence et supprime le besoin d'un code standard.

Au minimum, vous devez spécifier __eq__ et __lt__ opérations pour que cela fonctionne. Ensuite, utilisez juste 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]

11
2018-02-11 11:35



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.


Mettre à jour: Bien que cette méthode fonctionne, je pense que la solution de Triptych est mieux adaptée à votre cas, car plus simple.


9
2017-12-31 16:45