Anfängerfrage: Objektliste mit zwei Attributen sortieren

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
angwedh
User
Beiträge: 20
Registriert: Dienstag 16. März 2010, 08:04

Hallo Zusammen
Und wieder habe ich eine Anfängerfrage, bei der ich selbst mit Google nicht wirklich weiterkomme.

Ich habe eine Liste mit Objekten, jeweils mit zwei Zahlen a und b. Diese Liste möchte ich nun sortieren und zwar zuerst nach a und falls die zwei a von zwei Objekten gleich ist, soll b die Entscheidung fällen.

Zur Zeit sortiere ich die Liste mit einem key, jedoch nur nach a. Nun gibt es ja mehrere Möglichkeiten, entweder mache ich es mit einer while schlaufe in dem ich dann alle elemente mit a == a nochmals vergleiche (was je nach Anzahl von Objekten zu sehr vielen Schlaufendurchgängen führen würde) oder ich mache es über eine key funktion die irgendwie zwei Objekte vergleicht, was ich aber noch nicht ganz begriffen habe, denn über key kann man ja nur ein Element jeweils ausgeben (Das hier verstehe ich nicht ganz).

Nun die erste Version ist sicher machbar, jedoch muss mein Script in naher Zukunft viele Zahlen-Objekte vergleichen, was sehr lange dauren könnte und die Version mit key wäre wohl eleganter.

Hier mein Code:

Code: Alles auswählen

#Sortieren von Objekten in einer Liste anhand von zwei Attributen

class Numbers(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __lt__(self, other):
        if self.a == other.a:
            if self.b < other.b:
                return True
            elif self.b > other.b:
                return False
            else:
                print("Beide haben die gleichen Werte!!!")
                return True
        elif self.a < other.a:
            return True
        else:
            return False

def sort(liste):
    #Sortiert die Liste anhand des Attributes a, sollte aber b auch einbeziehen!
    liste.sort(key= lambda t: t.a, reverse=True)
            
def new(a, b, liste):
    new_number = Numbers(a, b)
    liste.append(new_number)

def show(liste):
    for i in liste:
        print(i.a, "   ", i.b)

#Hauptprogramm mit ein paar Beispielwerten als Test
li = []
new(10, 12, li)
new(13, 13, li)
new(10, 11, li)
new(10, 14, li)
new(10, 11, li)
print("Unsortierte Liste:")
show(li)
print("Nun sortiert:")
sort(li)
show(li)

Was ist hier die bessere Lösung, bzw. wie würde konkret die Version mit .sort(key=X) aussehen?

Sorry für die Anfängerfrage, aber ich versuche nun seit zwei Tagen eine gute Lösung zu finden...

Grüsse
angwedh
Benutzeravatar
gkuhl
User
Beiträge: 600
Registriert: Dienstag 25. November 2008, 18:03
Wohnort: Hong Kong

Hallo,

gibt es einen speziellen Grund, warum du nicht einfach eine Liste von Tuple nimmst?

Code: Alles auswählen

In [1]: lst = []

In [2]: lst.append((10,12))

In [3]: lst.append((13,13))

In [4]: lst.append((10,11))

In [5]: lst.append((10,14))

In [6]: lst.append((10,11))

In [7]: lst
Out[7]: [(10, 12), (13, 13), (10, 11), (10, 14), (10, 11)]

In [8]: lst.sort()

In [9]: lst
Out[9]: [(10, 11), (10, 11), (10, 12), (10, 14), (13, 13)]
Grüße
Gerrit
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Du musst nur deine `Numbers`-Klasse aendern (die besser `Number` heissen sollte), indem du die anderen rich-comparison Methoden implementierst.
(und deine `sort`-Funktion brauchst du dann nicht mehr)

Dein `__lt__` ist auch besser so zu loesen:

Code: Alles auswählen

def __lt__(self, other):
    return self.a < other.a or (self.a == other.a and self.b < other.b)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Wie wäre es damit:

Code: Alles auswählen

In [2]: class Number(object):
   ...:     def __init__(self, a, b):
   ...:         self.a = a
   ...:         self.b = b

In [3]: numbers = [Number(10, 12), Number(13, 13), Number(10, 11)]

In [18]: numbers.sort(key=operator.attrgetter("a", "b"))

In [19]: for n in numbers:
   ....:     print n.a, n.b
   ....:
   ....:
10 11
10 12
13 13
Panke
User
Beiträge: 185
Registriert: Sonntag 18. März 2007, 19:26

Ich denke, wenn es auf Zahlen (Nummern) eine Ordnung gibt, dann sollte man einer Klasse diese auch spendieren.
angwedh
User
Beiträge: 20
Registriert: Dienstag 16. März 2010, 08:04

Hallo Zusammen
Vielen Dank für die Antworten! Ich habe meiner Klasse nun alle Vergelichsoperatoren gegeben und siehe da, es funktioniert alles bestens! Gibt es irgendwo eine Beschreibung, wie die sort() methode von Listen funktioniert? Würde mich interessieren.

Hier nun den Code wie es nach mir funktioniert:

Code: Alles auswählen

#Sortieren von Objekten in einer Liste anhand von zwei Attributen

class Number(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __eq__(self, other):
        return self.a == other.a and self.b == other.b
    def __ne__(self, other):
        return self.a != other.a or (self.a == other.a and self.b != other.b)
    def __lt__(self, other):
        return self.a < other.a or (self.a == other.a and self.b < other.b)
    def __le__(self, other):
        return self.a < other.a or (self.a == other.a and self.b < other.b) or (self.a == other.a and self.b == other.b)
    def __gt__(self, other):
        return self.a > other.b or (self.a == other.a and self.b > other.b)
    def __ge__(self, other):
        return self.a > other.a or (self.a == other.a and self.b > other.b) or (self.a == other.a and self.b == other.b)

def sort(liste):
    liste.sort(reverse=True)
            
def new(a, b, liste):
    new_number = Number(a, b)
    liste.append(new_number)

def show(liste):
    for i in liste:
        print(i.a, "   ", i.b)

#Hauptprogramm mit ein paar Beispielwerten als Test
li = [Number(105,12),Number(16,12),Number(15,12),Number(10,12),Number(10,11),Number(10,15),Number(10,12)]

print("Unsortierte Liste:")
show(li)
print("Nun sortiert:")
sort(li)
show(li)
btw: gibt es eine Möglichkeit, dass man die Vergleichsoperatoren für eine eigene Klasse mehrfach überlädt? So dass man je nach Fall a und b als Vergleichswerte verwendet oder eben c und d?

grüsse
angwedh
Panke
User
Beiträge: 185
Registriert: Sonntag 18. März 2007, 19:26

Methoden überladen geht nicht.

sort ist in der Dokumentation beschrieben.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Panke hat geschrieben:Methoden überladen geht nicht.
Man kann es allerdings simulieren, indem man die Unterscheidung innerhalb der Methode trifft.

Ich habe neulich auch einen Blogpost gelesen wo das anhand von Dekoratoren und Typen (und mit mehreren Funktionen) gemacht wurde. Aber meist ist eine extra Funktion sowieso besser.

Wenn du den Algorithmus dahinter meinst: Timsort
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Die Antwort auf gkuhls Vorschlag würde mich schon noch interessieren. Seine Lösung wäre nämlich auch mein erster Gedanke gewesen.
MfG
HWK
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

cofi hat geschrieben:
Panke hat geschrieben:Methoden überladen geht nicht.
Man kann es allerdings simulieren, indem man die Unterscheidung innerhalb der Methode trifft.
Oder im Dekorator, was etwas eleganter scheint.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
angwedh
User
Beiträge: 20
Registriert: Dienstag 16. März 2010, 08:04

Hallo Zusammen
Vielen Dank für die Antworten. Der Grund wieso ich nicht eine Liste mit Tupeln nahm ist ziemlich einfach. Da dies nur ein Testscript war für ein anstehendes grösseres Projekt ist und in diesem dann die Objekte von der Klasse Number etwas komplexer werden (und Methoden haben etc), habe ich auch in diesem Script Objekte für die zwei Zahlen instanziert. Natürlich wäre es normalerweise viel einfacher hier mit Tupeln zu arbeiten.

Ich werde mir mal die Sache mit den Dekoratoren anschauen, nicht dass ich es nächstens brauche, ist aber sicher gut zu wissen.

grüsse
angwedh
Antworten