Hoe sorteer ik een lijst met objecten op basis van een attribuut van de objecten?

Ik heb een lijst met Python-objecten die ik wil sorteren op een attribuut van de objecten zelf. De lijst ziet er als volgt uit:

>>> ut
[<Tag: 128>, <Tag: 2008>, <Tag: <>, <Tag: actionscript>, <Tag: addresses>,
 <Tag: aes>, <Tag: ajax> ...]

Elk object heeft een telling:

>>> ut[1].count
1L

Ik moet de lijst sorteren op aflopend aantal.

Ik heb hiervoor verschillende methoden gezien, maar ik ben op zoek naar de beste werkwijzen in Python.


Antwoord 1, autoriteit 100%

# 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)

Meer over sorteren op sleutel.


Antwoord 2, autoriteit 6%

Een manier die het snelst kan zijn, vooral als uw lijst veel records bevat, is door operator.attrgetter("count")te gebruiken. Dit kan echter draaien op een pre-operatorversie van Python, dus het zou leuk zijn om een terugvalmechanisme te hebben. Misschien wilt u dan het volgende doen:

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

Antwoord 3, autoriteit 5%

Lezers moeten opmerken dat de sleutel = methode:

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

is vele malen sneller dan het toevoegen van rijke vergelijkingsoperators aan de objecten. Ik was verrast om dit te lezen (pagina 485 van “Python in een notendop”). U kunt dit bevestigen door tests op dit kleine programma te gebruiken:

#!/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

Mijn, zeer minimale, tests tonen de eerste soort meer dan 10 keer langzamer, maar het boek zegt dat het slechts ongeveer 5 keer langzamer in het algemeen is. De reden dat ze zeggen is te wijten aan de sterk optimaliseert sortalgoritme die wordt gebruikt in Python (timsort ).

Nog steeds, het is heel vreemd dat .Sort (lambda) sneller is dan gewoon oud .Sort (). Ik hoop dat ze dat oplossen.


Antwoord 4, Autoriteit 4%

Object-georiënteerde aanpak

Het is een goede gewoonte om het object sorteren van logica te maken, indien van toepassing, een eigenschap van de klasse in plaats van in elk geval de bestellen vereist.

Dit zorgt voor consistentie en verwijdert de behoefte aan boilerplatecode.

Minimaal moet u __eq__en __lt__Operations hiervoor opgeven. Gebruik vervolgens gewoon 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]

Antwoord 5, autoriteit 3%

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

Antwoord 6

Het lijkt veel op een lijst met instanties van het Django ORM-model.

Waarom sorteert u ze niet als volgt op een zoekopdracht:

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

Antwoord 7

Voeg uitgebreide vergelijkingsoperatoren toe aan de objectklasse en gebruik vervolgens de sort()-methode van de lijst.
Zie rijke vergelijking in python.


Update: hoewel deze methode zou werken, denk ik dat de oplossing van Triptych beter geschikt is voor uw geval omdat het veel eenvoudiger is.


Antwoord 8

Als het kenmerk waarop u wilt sorteren een eigenschapis, kunt u voorkomen dat u operator.attrgetterimporteert en de fgetmethode in plaats daarvan.

Bijvoorbeeld, voor een klasse Circlemet een eigenschap radiuskunnen we een lijst van circlesals volgt sorteren op radii:

result = sorted(circles, key=Circle.radius.fget)

Dit is niet de meest bekende functie, maar bespaart me vaak een lijn met de import.

Other episodes