1229 Stimmen

Wie sortiere ich eine Liste von Objekten basierend auf einem Attribut der Objekte?

Ich habe eine Liste von Python-Objekten, die ich nach einem bestimmten Attribut jedes Objekts sortieren möchte:

[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]

Wie sortiere ich die Liste nach .count absteigend?

7 Stimmen

Sortieranleitung für diejenigen, die mehr Informationen über das Sortieren in Python suchen.

1 Stimmen

Abgesehen von operator.attrgetter ('attribut_name') können Sie auch Funktoren als Schlüssel verwenden, wie z.B. object_list.sort(key=my_sorting_functor ('my_key')), wobei die Implementierung absichtlich ausgelassen wird.

1948voto

Kenan Banks Punkte 196831

Um die Liste an Ort und Stelle zu sortieren:

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

Um eine neue Liste zurückzugeben, verwenden Sie sorted:

new_list = sorted(orig_list, key=lambda x: x.count, reverse=True)

Erklärung:

  • key=lambda x: x.count sortiert nach Anzahl.
  • reverse=True sortiert in absteigender Reihenfolge.

Mehr zu Sortieren nach Schlüsseln.

2 Stimmen

Kein Problem. Übrigens, wenn Muhuk recht hat und es sich um eine Liste von Django-Objekten handelt, solltest du seine Lösung in Betracht ziehen. Für den allgemeinen Fall des Sortierens von Objekten ist meine Lösung wahrscheinlich bewährte Praxis.

66 Stimmen

Bei großen Listen erzielen Sie eine bessere Leistung, wenn Sie operator.attrgetter('count') als Ihren Schlüssel verwenden. Dies ist nur eine optimierte (niedrigere Ebene) Form der Lambda-Funktion in dieser Antwort.

8 Stimmen

Vielen Dank für die großartige Antwort. Falls es sich um eine Liste von Wörterbüchern handelt und "count" einer ihrer Schlüssel ist, muss es wie folgt geändert werden: ut.sort(key=lambda x: x['count'], reverse=True)

117voto

tzot Punkte 86792

Eine Möglichkeit, die am schnellsten sein kann, insbesondere wenn Ihre Liste viele Einträge enthält, besteht darin, operator.attrgetter("count") zu verwenden. Dies könnte jedoch auf einer Vor-Operator-Version von Python ausgeführt werden, daher wäre es gut, einen Ausfallmechanismus zu haben. Dann könnten Sie Folgendes tun:

try: import operator
except ImportError: keyfun= lambda x: x.count # Verwenden Sie ein Lambda, wenn kein Operator-Modul vorhanden ist
else: keyfun= operator.attrgetter("count") # Verwenden Sie den Operator, da er schneller als Lambda ist

ut.sort(key=keyfun, reverse=True) # sortieren Sie direkt vor Ort

8 Stimmen

Hier würde ich den Variablennamen "keyfun" anstelle von "cmpfun" verwenden, um Verwirrung zu vermeiden. Die sort() Methode akzeptiert auch eine Vergleichsfunktion über das Argument cmp=.

0 Stimmen

Dies scheint nicht zu funktionieren, wenn dem Objekt dynamisch hinzugefügte Attribute vorliegen (wenn Sie self.__dict__ = {'some':'dict'} nach der __init__-Methode durchgeführt haben). Ich weiß nicht, warum es anders sein sollte, allerdings.

0 Stimmen

@tutuca: Ich habe die Instanz __dict__ noch nie ersetzt. Beachten Sie, dass die Konzepte "ein Objekt mit dynamisch hinzugefügten Attributen haben" und "das __dict__-Attribut eines Objekts setzen" fast orthogonal sind. Ich sage das, weil Ihr Kommentar zu implizieren scheint, dass das Setzen des __dict__-Attributs eine Voraussetzung für das dynamische Hinzufügen von Attributen ist.

98voto

Jose M Vidal Punkte 8338

Leser sollten beachten, dass die Methode key=:

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

viele Male schneller ist als das Hinzufügen von Rich-Vergleichsoperatoren zu den Objekten. Ich war überrascht dies zu lesen (Seite 485 von "Python in a Nutshell"). Sie können dies bestätigen, indem Sie Tests mit diesem kleinen Programm durchführen:

#!/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)] # etwa 6,1 Sekunden
longList2 = longList[:]

longList.sort() # etwa 52 - 6.1 = 46 Sekunden
longList2.sort(key = lambda c: c.count) # etwa 9 - 6.1 = 3 Sekunden

Meine sehr minimalen Tests zeigen, dass die erste Sortierung mehr als 10 Mal langsamer ist, aber das Buch sagt, dass sie im Allgemeinen nur etwa 5 Mal langsamer ist. Der Grund, den sie angeben, ist der hoch optimierte Sortieralgorithmus, der in Python verwendet wird (timsort).

Dennoch ist es sehr seltsam, dass .sort(lambda) schneller ist als das gewöhnliche .sort(). Ich hoffe, sie beheben das.

4 Stimmen

Die Definition von __cmp__ entspricht einem Aufruf von .sort(cmp=lambda), nicht .sort(key=lambda), daher ist es überhaupt nicht merkwürdig.

0 Stimmen

@tzot hat genau recht. Der erste Sortiervorgang muss Objekte immer wieder miteinander vergleichen. Der zweite Sortiervorgang greift jedes Objekt nur einmal ab, um seinen Zählwert zu extrahieren, und führt dann eine einfache numerische Sortierung durch, die sehr optimiert ist. Ein fairerer Vergleich wäre longList2.sort(cmp = cmp). Ich habe das ausprobiert und es hat fast genauso gut funktioniert wie .sort(). (Anmerkung: Beachten Sie auch, dass der "cmp" Sortierparameter in Python 3 entfernt wurde.)

4 Stimmen

cmp wurde in Python 3 veraltet: docs.python.org/3/howto/…

83voto

jpp Punkte 146159

Objektorientierter Ansatz

Es ist eine gute Praxis, die Objektsortierlogik, falls zutreffend, als Eigenschaft der Klasse zu erstellen, anstatt sie in jede Instanz einzubauen, in der die Sortierung erforderlich ist.

Dies stellt Konsistenz sicher und beseitigt die Notwendigkeit für Boilerplate-Code.

Mindestens sollten Sie __eq__ und __lt__ Operationen angeben, damit dies funktioniert. Dann verwenden Sie einfach 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]

3 Stimmen

Das ist genau das, wonach ich gesucht habe! Könnten Sie uns auf einige Dokumentationen verweisen, die erklären, warum __eq__ und __lt__ die Mindestimplementierungsanforderungen sind?

4 Stimmen

@FriendFX, ich glaube, dass es durch dieses impliziert wird: • Die Sortierroutinen verwenden garantiert __lt__() beim Vergleichen von zwei Objekten...

2 Stimmen

@FriendFX: Siehe portingguide.readthedocs.io/en/latest/comparisons.html für Vergleiche und Sortieren

46voto

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X