Seite 1 von 1

Anfängerfrage: Objektliste mit zwei Attributen sortieren

Verfasst: Donnerstag 25. März 2010, 09:19
von angwedh
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

Verfasst: Donnerstag 25. März 2010, 09:41
von gkuhl
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

Verfasst: Donnerstag 25. März 2010, 09:44
von cofi
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)

Verfasst: Donnerstag 25. März 2010, 09:53
von Hyperion
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

Verfasst: Donnerstag 25. März 2010, 11:44
von Panke
Ich denke, wenn es auf Zahlen (Nummern) eine Ordnung gibt, dann sollte man einer Klasse diese auch spendieren.

Verfasst: Donnerstag 25. März 2010, 12:33
von angwedh
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

Verfasst: Donnerstag 25. März 2010, 14:27
von Panke
Methoden überladen geht nicht.

sort ist in der Dokumentation beschrieben.

Verfasst: Donnerstag 25. März 2010, 14:54
von cofi
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

Verfasst: Donnerstag 25. März 2010, 18:45
von HWK
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

Verfasst: Freitag 26. März 2010, 00:59
von Leonidas
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.

Verfasst: Freitag 26. März 2010, 10:07
von angwedh
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