Sortieren von Objekten

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.
Aeron
User
Beiträge: 7
Registriert: Montag 8. Juni 2009, 18:27

Hallo,
hier erstmal mein Code:

Code: Alles auswählen

class Highscore(list):
    HighscoreEntry=[]

    def __init__(self, HighscoreEntry):
       self.HighscoreEntry+=[HighscoreEntry]
              
    def PlayerAverageScore(self, p):
        count_scores=0
        total_score=0
        for entry in self.HighscoreEntry:

            if (p==entry.PlayerName):
                count_scores+=1
                total_score+=int(entry.Score)       
        if(count_scores>0):      
            return '%.2f'%(round(float(total_score)/float(count_scores), 2))

        else:
            return 0
    
    def TotalAverageScore(self):
        count_scores=0
        total_score=0
        for entry in self.HighscoreEntry:
            count_scores+=1
            total_score+=int(entry.Score)
        if(count_scores>0):      
            return '%.2f'%(round(float(total_score)/float(count_scores), 2))
        else:
            return 0
    def SortHighscore(self):
        self.sort()

class HighscoreEntry(Highscore):
    obj_counter=0
        
    def __init__(self, n, s, d):
        self.PlayerName=n
        self.Score=s
        self.Date=d
        self.obj_counter+=1
            
    def __del__(self):
        self.obj_counter-=1
        
    def __lt__(self, other):
        return self.Score < other.Score
    
    def __le__(self, other):
        return self.Score <= other.Score
    
    def __eq__(self, other):
        return self.Score == other.Score
    
    def __ne__(self, other):
        return self.Score != other.Score
    
    def __gt__(self, other):
        return self.Score > other.Score
    
    def __ge__(self, other):
        return self.Score >= other.Score
            
    def getPlayerName(self):
        return self.PlayerName
    def getScore(self):
        return self.Score
    def getDate(self):
        return self.Date

file = open('../highscore.txt','r')
i=0;
Entry={}

for line in file:
    data=string.split(line, ',')
    Entry[i]=HighscoreEntry(data[0], data[1], data[2])
    h=Highscore(Entry[i])
    i+=1
file.close();

Ich habe eine Highscore-Klasse die aus einer Textdatei gefüllt wird und möchte nun die einzelnen Einträge nach den Punkten also nach "Score" sortieren. Ich habe dafür die MagicMethods überschrieben, aber wenn ich h.sort() aufrufe tut sich nichts an der Reihenfolge ändern. Wisst ihr woran das liegt, könnt ihr mir irgendwie weiterhelfen? Wäre echt super!
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

du musst __cmp__ implementieren.

http://wiki.python.org/moin/HowTo/Sorting
http://www.kinderpornos.info
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

ich frage mich grade: wie würde man denn in python von int erben?
int ist ja ein type. und eine wrapper-klasse scheint es nicht zu geben. muss ich da mit diesem metaclass-zauber hantieren?

also konkret wie würde man das machen wenn man hier so eine highscore-klasse hat die die ganzen magic-member von int implementieren soll...
http://www.kinderpornos.info
Aeron
User
Beiträge: 7
Registriert: Montag 8. Juni 2009, 18:27

Mhh ich blicke jetzt irgendwie nicht ganz was ich in __cmp__ genau implementieren muss...

Code: Alles auswählen

    def __cmp__(self, other): 
        return cmp(self.Score, other.Score)
soetwas würde ich jetzt aus deinem Link entnehmen, was ich noch machen müsste, aber das funktioniert nicht

Nach http://openbook.galileocomputing.de/pyt ... 12_003.htm
hab ich auch folgendes probiert:

Code: Alles auswählen

    def __cmp__(self, other):
        if(self.Score>other.Score):
            return 1
        if(self.Score==other.Score):
            return 0
        if(self.Score>other.Score):
            return -1
leider auch ohne Erfolg :-( Aber da stand halt das die Methode halt entsprechende Werte zurückgeben muss und so, darum weiß ich jetzt eigentlich überhaupt nicht, was die Methode zurückliefern soll. Weil das ja nicht funktioniert. Könntest du mir noch nen kleinen Hinweis geben ^^?
Aeron
User
Beiträge: 7
Registriert: Montag 8. Juni 2009, 18:27

Ah meinst du, dass ich die Elemente bevor ich sie verlgeiche nach Integer casten muss? Damit sie auch als Zahl wargenommen werden?
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

was ist denn das problem? "geht nicht" hilft nicht.

nein, du musst die nicht casten, wenn die vom gleichen typ sind (müssten sie ja) und sie sich vergleichen lassen. (das nehme ich mal an)
Zuletzt geändert von Dill am Montag 8. Juni 2009, 18:57, insgesamt 1-mal geändert.
http://www.kinderpornos.info
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Dill hat geschrieben:ich frage mich grade: wie würde man denn in python von int erben?
So:

Code: Alles auswählen

>>> class MyInt(int):
...     def __new__(cls, val):
...         return int.__new__(cls, val)
...     def __str__(self):
...         return 'my int has the value: %d' % self
... 
>>> my_int = MyInt(42)
>>> my_int
42
>>> print my_int
my int has the value: 42
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Hinweis 1: Lass die Klammern bei Vergleichen weg.
Hinweis 2: Benutze keine Strichpunkte.
Hinweis 3: Überschreibe keine built-ins (`file`)
Hinweis 4: Schau dir mal [wiki]PEP 8 (Übersetzung)[/wiki] oder das Original an
Hinweis 5: Vergiss das Openbook, wenn du wissen willst warum such im Forum nach Openbook
Hinweis 6: Langen Code bitte nach http://paste.pocoo.org/ auslagern.

Eigentlich müsste die `sort`-Methode rich comparison benutzen, die du ja
implementiert hast ... Welche Version benutzt du denn?

Daneben wüsste ich gerne, warum du von `list` ableitest und nicht einfach composition benutzt (eine Liste als Attribut).

@Dill für `int` muss man sich um `__new__` kümmern: http://www.voidspace.org.uk/python/webl ... html#e1014
BlackJack

@Aeron: Vergiss den Tipp von Dill "halb". Daran liegt's nicht, aber die `__cmp__()`-Methode zu implementieren ist weniger als die ganzen anderen Methoden zum Vergleichen zu implementieren.

Dein Problem scheint mir eher zu sein *was* Du sortierst. Du erbst von `list` und *das* Exemplar wird dann auch sortiert, nur sind da gar keine Einträge drin, denn die packst Du in das Klassenattribut `HighscoreEntry`.

Der ganze Klassenentwurf ist Murks. Einen Konstruktor nur dazu zu missbrauchen um eine Element zu einem Klassenattribut hinzuzufügen ist falsch. Das `Highscore` von `list` erbt, wird nirgends verwendet, also kann man das auch bleiben lassen. Die Einträge sollten in einem Attribut auf dem Exemplar, also für jedes Exemplar verschieden, gespeichert werden.

Das runden in `PlayerAverageScore` ist überflüssig. Und je nach Situation eine Zahl oder eine Zeichenkette zurückzugeben ist eine schlechte Idee. Der Rückgabewert sollte möglichst immer vom gleichen Typ sein.

Die Namen entsprechen nicht der üblichen Konvention. Insbesondere Namen die mit einem Grossbuchstaben beginnen, sollten für Klassen vorbehalten bleiben.

`HighscoreEntry` sollte nicht von `Highscore` erben. Vererben drückt eine "ist-ein(e)"-Beziehung aus, und ein einzelner Eintrag ist eben keine ganze Bestenliste.

Der `obj_counter` scheint mir hier überflüssig, ausserdem funktioniert das so nicht, denn `__del__()` wird nicht garantiert und zuverlässig aufgerufen.

Die Namen der Argumente von den meisten Methoden sind zu kurz. Namen sollten auch was aussagen und nicht Fragezeichen beim Leser hinterlassen.

Die Getter sind in Python unüblich. Die Getter, die sporadischen Semikolons, und die Namensgebung machen mir den Eindruck, dass Du versuchst eine andere Sprache in Python zu schreiben. Python ist nicht C++ oder Java. ;-)

Warum ein Dictionary für `Entry` und keine Liste? Und warum speicherst Du die in einem Dictionary *und* dem Klassenattribut in `Highscore`?

Funktionen aus dem `string`-Modul, die es auch als Methoden auf Zeichenketten gibt, sollte man nicht mehr benutzen.
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

huch, da war ichzu faul mir den code anzuschauen, dabei hätte schon ein blick in die erste zeile genügt... :oops:

danke don, das muss ich mir in eine ruhigen minute mal anschauen.
http://www.kinderpornos.info
Aeron
User
Beiträge: 7
Registriert: Montag 8. Juni 2009, 18:27

cofi hat geschrieben:Eigentlich müsste die `sort`-Methode rich comparison benutzen, die du ja
implementiert hast ... Welche Version benutzt du denn?

Daneben wüsste ich gerne, warum du von `list` ableitest und nicht einfach composition benutzt (eine Liste als Attribut).
Benutze Phython 2.6. Hatte jetzt von list abgeleitet weil sonst .sort() gar nicht implementiert war :) Eine Liste habe ich doch mit HighscoreEntry=[] praktisch als Attribut in der Highscore-Klasse oder nicht?

Mhh habe die Veerbung jetzt entfernt und die Hinweise alle beachtet und zum großteil schon umgesetzt :)
Einen Konstruktor nur dazu zu missbrauchen um eine Element zu einem Klassenattribut hinzuzufügen ist falsch.
Wie kann ich das alternativ denn umsetzen wenn ich die beiden Klassen Highscore und HighscoreEntry behalten will?

Aber warum die Sortierung konkret nicht geht weiß hier keiner oder? Oder will es nur niemand verraten :P?


h.sort kann ich jetzt nicht mehr aufrufen, wenn ich die Klassen mit

Code: Alles auswählen

class Highscore:
und

Code: Alles auswählen

class HighscoreEnty:
deklariere
Benutzeravatar
Dill
User
Beiträge: 470
Registriert: Mittwoch 10. Januar 2007, 14:52
Wohnort: Köln

blackjack hat doch geschrieben warum es nicht funktioniert.
http://www.kinderpornos.info
BlackJack

@Aeron: Ich habe doch schon verraten warum es nicht funktioniert: Dein `sort()`-Aufruf sortiert eine leere Liste, die Du auch sonst gar nicht weiter verwendest.

Und ja, eine Liste hast Du als Attribut der Klasse. Die gehört aber bei einem ordentlichen OO-Entwurf nicht an die Klasse, sondern an ein Exemplar einer Bestenliste gebunden.

Wenn Du `sort()` auf einem `Highscore`-Exemplar aufrufen willst, dann muss es darauf halt auch implementiert werden. Vorzugsweise als delegation an das Attribut, dass die Elemente enthält. Wenn das eine Liste ist, dann kennt die ja auch eine `sort()`-Methode.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Das `sort` hat nicht funktioniert, weil du unglaublich viele Sachen durcheinander geworfen hast.

Wenn du composition benutzt löst sich dein Problem in einem Logikwölkchen auf. Es nützt eben nichts, wenn man nur erbt.

Dein `sort` kannst du ganz einfach mit einem

Code: Alles auswählen

def sort(self):
    self.entries.sort()
haben (wobei self.entries deine Liste ist)

Dazu solltest du dir anschauen welche ``__*__`` Methoden du implementieren musst, dass sich deine Klasse wie eine Liste verhält (wobei die Methoden dann ein proxy zu der internen Liste sein sollten !).

Code: Alles auswählen

class Highscore:
ist ganz schlecht, weil das `old-style` ist, du solltest deine Klasse von `object` ableiten. Du solltest dir mal das Tutorial anschauen und am besten alles aus dem Openbook zu Klassen vergessen.
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

cofi hat geschrieben:

Code: Alles auswählen

class Highscore:
ist ganz schlecht, weil das `old-style` ist, du solltest deine Klasse von `object` ableiten. Du solltest dir mal das Tutorial anschauen und am besten alles aus dem Openbook zu Klassen vergessen.
Wobei die oldstyle/newstyle-Geschichte mit Python 3 ja hinfällig geworden ist, wenn ich das richtig verstanden habe.
Aeron
User
Beiträge: 7
Registriert: Montag 8. Juni 2009, 18:27

Vielen Dank schonmal soweit, ich verstehe inzwischen wo das Problem liegt :) Habe versucht das jetzt zu verbessern, indem ich schonmal die Liste mit den einzelnen Einträgen an ein Exemplar der Bestenliste gebunden habe. Hab die sort()-Methode auch schon eingebaut.

Allerdings wird jetzt immer nur ein Element gespeichert in den Objekten, also nur ein eintrag in der Bestenliste, wisst ihr woran das liegt? Bin ich auf dem richtigen Weg, das die Sortierung funktioniert oder denke ich immernoch falsch?

Code: Alles auswählen

class Highscore(object):
    
    def __init__(self):
        self.HighscoreEntry=[]
    
    def AddEntry(self, Entry):
       self.HighscoreEntry+=[Entry]
              
    def PlayerAverageScore(self, p):
        count_scores=0
        total_score=0
        for entry in self.HighscoreEntry:

            if (p==entry.PlayerName):
                count_scores+=1
                total_score+=int(entry.Score)       
        if(count_scores>0):      
            return '%.2f'%(round(float(total_score)/float(count_scores), 2))

        else:
            return 0
    
    def TotalAverageScore(self):
        count_scores=0
        total_score=0
        for entry in self.HighscoreEntry:
            count_scores+=1
            total_score+=int(entry.Score)
        if(count_scores>0):      
            return '%.2f'%(round(float(total_score)/float(count_scores), 2))
        else:
            return 0
    def sort(self):
        self.HighscoreEntry.sort()

class HighscoreEntry(object):
        
    def __init__(self, n, s, d):
        self.PlayerName=n
        self.Score=s
        self.Date=d
                   
    def __lt__(self, other):
        return self.Score < other.Score
    
    def __le__(self, other):
        return self.Score <= other.Score
    
    def __eq__(self, other):
        return self.Score == other.Score
    
    def __ne__(self, other):
        return self.Score != other.Score
    
    def __gt__(self, other):
        return self.Score > other.Score
    
    def __ge__(self, other):
        return self.Score >= other.Score
            
    def getPlayerName(self):
        return self.PlayerName
    def getScore(self):
        return int(self.Score)
    def getDate(self):
        return self.Date

fobj = open('../highscore.txt','r')
i=0;


for line in fobj:
    data=string.split(line, ',')
    h=Highscore()
    h.AddEntry(HighscoreEntry(data[0], data[1], data[2]))
    i+=1
fobj.close()
Auslesen kann ich die Objekte doch mittels folgeder Schleife ausserhalb der Klassen oder?

Code: Alles auswählen

for entry in h.HighscoreEntry:
   entry.getPlayerName()
als Beispiel für die Ausgabe der Namen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

numerix hat geschrieben:Wobei die oldstyle/newstyle-Geschichte mit Python 3 ja hinfällig geworden ist, wenn ich das richtig verstanden habe.
Das schon, aber er benutzt ja noch 2.6 ;)

Entschuldige, aber an der Lesbarkeit deines Codes hapert es kräftig, deswegen werde ich nichts dazu sagen, bis du die bisherigen Hinweise auch umsetzt in deinem Code.

(Das Posten wird übrigens immer schwieriger, weil du deinen Code nicht auslagerst.)
Benutzeravatar
numerix
User
Beiträge: 2696
Registriert: Montag 11. Juni 2007, 15:09

cofi hat geschrieben:
numerix hat geschrieben:Wobei die oldstyle/newstyle-Geschichte mit Python 3 ja hinfällig geworden ist, wenn ich das richtig verstanden habe.
Das schon, aber er benutzt ja noch 2.6 ;)
Klar, sollte auch kein Einwand gegen deinen Hinweis dazu sein. Nur 'ne Anmerkung mit Blick nach vorne ...
BlackJack

@Aeron: Geh doch den Quelltext auf Modulebene mal in Gedanken durch. Was passiert da bei jedem Schleifendurchlauf? Wieviele `Highscore`-Objekte brauchst Du? Wieviele erstellst Du?
Aeron
User
Beiträge: 7
Registriert: Montag 8. Juni 2009, 18:27

Mhhh ich brauche ein Highscore-Objekt, das halt mehrere HighscoreEntry Objekte enthält. Und da war auch schon der Fehler warums nur ein Element gab, hab bei jedem Schleifendurchlauf ein neues Objekt unter glreichem Namen angelegt :) Dann kann natürlich auch nur ein Element von HighscoreEntry drinnen sein! Super, DANKESCHÖN :)

richtig, ists die erstellung der Objekte dann so:

Code: Alles auswählen

h=Highscore()
for line in fobj:
    data=string.split(line, ',')
    h.AddEntry(HighscoreEntry(data[0], data[1], data[2]))
    i+=1
fobj.close()

Sortieren will es allerdings immer noch nicht, schnüff :(

Weil zum beispiel meine Methode TotalAverageScore greift ja auch wieder auf die Liste der HighscoreEntry-Elemente zu und da funktioniert es auch. Also kann die Liste ja eigentlich nicht leer sein, oder was versteh ich da noch falsch?
Antworten