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.attrgetter
importeert en de fget
methode in plaats daarvan.
Bijvoorbeeld, voor een klasse Circle
met een eigenschap radius
kunnen we een lijst van circles
als 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.