[Im Aufbau] benchmark.py

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.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hab in Zeile 23 nen time.sleep(3) reingesetzt und die Auslastung ist immer ziemlich gleich (Bei mir zwischen 40 und 50% da ich eine X23800+ habe und Python anscheinend keine Dualcore CPUs unterstützt)

Läute ihr bringt mich wider auf eine Idee xD Wäre es nicht vielleicht gut, noch einzubauen wie viel Prozent die CPU ausgelastet wird, und das dann im Ranking anzeigen zu lassen? Gibt es in Python dafür ein Standard Modul?

lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Zeile 37 wahr comment (self.data[0][1]) und name (self.data[0][2]) vertauscht, hab das mal geändert.

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: UTF-8 -*-
# Coding by jens, BlackJack und XtraNine @www.python-forum.de
#
# Dank geht an jens, BlackJack und allen anderen aus diesem Thread,
# die mir dabei geholfen haben: 
# http://www.python-forum.de/viewtopic.php?p=48410#48410
#
# LG XtraNine

import time

class Benchmark:
    def __init__(self):
        self._functions = []
        self.data = []
        self.refTime = 0.0

    def AddFunc(self, ref, comment, *args):
        self._functions.append( [ref.__name__, comment, ref, args] )

    def Run(self):
        for name, comment, ref, args in self._functions:
            print "run: %10s - %-10s" % (name, comment),
            t1 = time.clock()
            ref(*args)
            t2 = time.clock() - t1
            self.data.append([t2, comment, name])
            print "OK (%.2fsec.)" % t2
            
        self.data.sort(reverse=True)
        self.refTime = bench.data[0][0]
        print "-"*80

    def PrintRanking(self):
        print "Funktion: %10s - %-10s ist am langsamste; Zeit %f sec" %(
            self.data[0][2], self.data[0][1], self.data[0][0]
        )
        for t, comment, name in self.data[1:]:
            percent = (100*self.refTime/t) - 100
            print "Funktion: %10s - %-10s ist um %.2f%% schneller; Zeit %f sec" % (
                name, comment, percent, t
            )


if __name__ == '__main__':
    def f1(loops):
        list = [str(i) for i in xrange(loops)]

    def f2(loops):
        list = []
        for i in xrange(loops):
            list.append(str(i))

    def f3(loops):
        list = []
        for i in range(loops):
            list.append(str(i))

    loops = 1000000
    bench = Benchmark()
    bench.AddFunc(f1, "LC", loops)
    bench.AddFunc(f2, "xrange", loops)
    bench.AddFunc(f3, "range", loops)
    bench.Run()
    bench.PrintRanking()

Code: Alles auswählen

run:         f1 - LC         OK (1.21sec.)
run:         f2 - xrange     OK (1.44sec.)
run:         f3 - range      OK (1.48sec.)
--------------------------------------------------------------------------------
Funktion:         f3 - range      ist am langsamste; Zeit 1.478379 sec
Funktion:         f2 - xrange     ist um 2.88% schneller; Zeit 1.437011 sec
Funktion:         f1 - LC         ist um 22.50% schneller; Zeit 1.206814 sec
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Wegen der CUP Ausslastung direkt ins Ranking anzeigen:
Hab da folgenden Thread gefunden -> http://www.python-forum.de/viewtopic.php?t=2639
Man könnte das vielleicht über die WinAPI daran kommen. Muss ich mal die Tage testen ob ich da was zusammengebaut krieg. Mit Linux kenne ich mich aber nicht aus, das müsste dann ein anderer übernehmen :p

lg
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hm. Vielleicht liegt es daran, das die einen Athlon X2 (DoppelKern) hab... btw. es werden bei mir bei allen drei Aktionen nur ein Kern genutzt. Aber ich glaube das liegt generell an Python.
Ich meine es gab da auf der Mailingliste mal ein Thread dazu...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi jens.

Das ist mir auch schon aufgefallen. Mein A64 X2 3800+, wird irgendwie nie von Python scripten 100% ausgelastet. Immer zu ca. 50% :-[

Ist den eine SMP Version von Python geplant, in naher Zukunft?
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hi, bin fertig mit den Docstrings :)

Hab noch eine Methode hinzugefügt die sich GetRanking nennt. Damit kann man sich das Ranking wahlweise als list von lists (=> [[], [], … ]) oder list von dicts (=>[{}, {}, …] zurückgeben lassen, falls man die Ausgabe selber gestallten möchte und woanders ausgeben will (z.B. eine Datei oder an ein listctrl vom wxPython, etc). Ich hab dazu die Funktion main() mit beispielen erweitert bzw. alles in die Funktion geschoben.

Alle Docstrings sind für epydoc ab 3.0alpha3. Deshalb nicht wundern wenn das alles auf pydoc komisch aussieht ;)

Falls ihr Zeit habt, könnt ihr mal alles durch epydoc jagen und euch die Dokumentation ansehen und mir feedback geben ob das alles verständlich beschrieben ist oder ob was zu ändern ist.

Falls ihr noch Vorschläge habt die man einbringen könnte oder Kritik, dann lasst es mich wissen. In der Zwischenzeit werde ich mal schauen ob man eine Anzeige der Prozessorauslastung mit implementieren kann.

BTW: Ich weiß das sich die Namen der Methoden nicht an PEP8 halten, die man ja alle lowercase und underscores benenn soll, aber mir gefällt es so besser und wxPython macht es ja auch so ;) ^^

lg xtra

Code ins LodgeIt ausgelagert.

Edit (Leonidas): Code ausgelagert, da der Thread sonst gesperrt hat (Software-Bug des Forums).
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:BTW: Ich weiß das sich die Namen der Methoden nicht an PEP8 halten, die man ja alle lowercase und underscores benenn soll, aber mir gefällt es so besser und wxPython macht es ja auch so ;) ^^
Genau das wollte ich grade anmängeln. Aber wxPython gilt nicht als Ausrede, denn wxPython ist ein dünner Wrapper um wxWidgets, von dem es das CamelCase erbt. Als Vorbildfunktion kann die API von wxPython auch nicht dienen, denn wxPython hat eine sehr unpythonische und wenig angenehme API (die sich eben nach C++ "anfühlt").

Wieso ich gegen die C++ API schimpfe?
Vergleiche mal die API von SAX oder insbesondere DOM (die aus der Java-Welt übernommen sind) mit der API von ElementTree.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

:(

Hab übrigens zu PEP8 nen Thread open: http://www.python-forum.de/topic-7781.h ... hlight=24h

...

Leo, Aber der Rest stimmt vom Modul oder sind da viel Sachen die noch nciht gut sind?

lg

EDIT: Was ist den LodgeIt? Muss man sich da anmelden und kann dann Code veröffentlichen? (EDIT:) --> Ok habs schon. Gute Sache :)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:Hab übrigens zu PEP8 nen Thread open: http://www.python-forum.de/topic-7781.h ... hlight=24h
Ja, ich guck gleich rein.
XtraNine hat geschrieben:Leo, Aber der Rest stimmt vom Modul oder sind da viel Sachen die noch nciht gut sind?
Das # EOF am ende finde ich etwas blöd, ansonsten fände ich es besser, wenn du in den DocStrings reST verwendest, denn diese Docutils-Syntax sieht beim lesen im Plaintext etwas seltsam aus.
XtraNine hat geschrieben:EDIT: Was ist den LodgeIt? Muss man sich da anmelden und kann dann Code veröffentlichen? (EDIT:) --> Ok habs schon. Gute Sache :)
Das ist der No-Paste-Service von blackbird, der Django und Pygments (wieder ein Projekt von birkenfeld und blackbird) verwendet. Den finde ich so gelungen, dass ich den jetzt zum offiziellen No-Paste-Services dieses Forums erkläre. Werde keinen zwingen ihn zu nutzen, ich kann aber dazu raten, denn es funktionier super :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Leonidas hat geschrieben:
XtraNine hat geschrieben:Hab übrigens zu PEP8 nen Thread open: http://www.python-forum.de/topic-7781.h ... hlight=24h
Ja, ich guck gleich rein.
XtraNine hat geschrieben:Leo, Aber der Rest stimmt vom Modul oder sind da viel Sachen die noch nciht gut sind?
Das # EOF am ende finde ich etwas blöd, ansonsten fände ich es besser, wenn du in den DocStrings reST verwendest, denn diese Docutils-Syntax sieht beim lesen im Plaintext etwas seltsam aus.
:? Hab gerade nach reST in wiki geschaut -> http://de.wikipedia.org/wiki/ReST
Ich fürchte ich verstehe aber nicht recht was du meinst. Hab doch da reST benutzt oder meinst du das diese B{test}, L{GetRanking()<Benchmark.GetRanking>} Strange aussehen?

Das lässt sich aber nicht vermeiden da man so bei epydoc die Sachen formatiert. zum beispiel verrede ich das L{...} sehr oft um nen Link zu ner Konstante/Methode/etc zu setzen. Ehrlich gesagt finde ich das nicht schön, das man den Sourcecode unbedingt _im_ Sourcecode dokumentieren muss. Wenn ich ne Doc mache, dann fällt die meistens sehr lang aus, weil ich das halt so gut wie möglich Dokumentieren will und nicht wie einige Docs von den Standardmodulen, die ja doch recht kurz sind, das man die eigentlich auch gleich weglassen könnte. Mir wäre es lieber das die sich damals was ausgedacht hätten, wo man das im separaten File macht :/
Z.B. Sourcefile und Docfile. Im Docfile ist dann ne Kurze Signatur von einer Klasse und den Methoden die dann dort dokumentiert werden. Im Sourcefile ist dann alles implementiert ohne unnötig alles mit Docstrings aufzublähen. ... Ich schweife wider ab ^^

Leonidas hat geschrieben:
XtraNine hat geschrieben:EDIT: Was ist den LodgeIt? Muss man sich da anmelden und kann dann Code veröffentlichen? (EDIT:) --> Ok habs schon. Gute Sache :)
Das ist der No-Paste-Service von blackbird, der Django und Pygments (wieder ein Projekt von birkenfeld und blackbird) verwendet. Den finde ich so gelungen, dass ich den jetzt zum offiziellen No-Paste-Services dieses Forums erkläre. Werde keinen zwingen ihn zu nutzen, ich kann aber dazu raten, denn es funktionier super :)


Super Sache :) Aber wie sucht man dort nach andere Sourcefiles? Wenn ich auf z.B. PYthon Klicke werden mir nur die angezeigt, die auch Python als Keyword hatten :? Gibt es da nicht so ne art Funktion die mir alle Sources anzeigt? Wie lange bleiben eigentlich die Sources gespeichert? Wenn das unbegrenzt ist, dan finde ich das richtig Super :bigok: Die nächsten Sachen werde ich dann dort auch pasten! :) Django ist von blacky ? :shock: Pygments kenne ich nicht. Gleich mal kucken.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:meinst du das diese B{test}, L{GetRanking()<Benchmark.GetRanking>} Strange aussehen?
Ja, genau. Denn um ehrlich zu sein, wird die Dokumentation meist im Plaintext angesehen, sei es im Editor oder durch help() und da ist dieses Markup unnötig viel "Noise".
XtraNine hat geschrieben:Ehrlich gesagt finde ich das nicht schön, das man den Sourcecode unbedingt _im_ Sourcecode dokumentieren muss.
Ich finde das eigentlich schon gut, ich finde auch Annotated Haskell lustig, eine Art ausführbare Dokumentation.
Nur wie gesagt, das epydoc-Markup ist hässlich.
XtraNine hat geschrieben:Super Sache :) Aber wie sucht man dort nach andere Sourcefiles?
Gar nicht, dazu ist es eigentlich auch nicht gedacht. Aber du kannst ja blackbird fragen, ob er so ein Feature auch noch implementiert.
XtraNine hat geschrieben:Wie lange bleiben eigentlich die Sources gespeichert? Wenn das unbegrenzt ist, dan finde ich das richtig Super
Ich glaube im Moment gibt es keine Löschfunktion.
XtraNine hat geschrieben:Django ist von blacky ?
Nein, aber Lodgeit nutzt Django.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

So, nun zum aktuellen Benchmark-Code:

Zeile 1:

Code: Alles auswählen

#!/usr/bin/python
würde ich durch

Code: Alles auswählen

#!/usr/bin/env python
ersetzen. Ist universeller.

Zeile 83: Was ist denn das? Warum ist in

Code: Alles auswählen

BenchError (Exception)
nicht

Code: Alles auswählen

BenchmarkError(Exception)
(welches PEP8 konform und auch noch beschreibender wäre, du siehst, Exception-Namen haben kaum Abkürzungen: AttributeError statt AttrError oder E_ATTRERR). Was macht die Klasse mehr als ein

Code: Alles auswählen

BenchmarkError(Exception): pass
? Ich habe das gefühl, dass es nur unnötiger Boilerplate-Code ist. Statt Error-Codes sollte man besser andere Exceptions definieren, denn Exceptions sind ja dafür da nichtssagende Error-Codes wie 1000 zu ersetzen.

Zeile 108: Warum definierst du hier alles mit einem Bodenstrich? Unnötig und macht es schwerer lesbar.

Zeile 140: nicht PEP8 kompatibel

Zeile 154: self._data[0][0], [0][0] sieht sehr nichtssagend aus, vielleicht kann an hier eine andere Datenstruktur verwenden? Zum Beipiel ein Dict, mit einer Listen innen drin, oder sowas ähnliches?

Zeile 159: nicht PEP8 kompatibel

Zeile 168: dito

Zeile 170: BM_GTRG_MODE_LIST finde ich völlig unnötig. Warum sollte man, wenn man schon die Möglichkeit hat, ein Dict zu bekommen, lieber eine undurchsichtige Struktur wie List haben wollen, bei der nicht sofort klar ist, welcher Index was für einen Wert repräsentiert? Klar, man kann Doku lesen, aber wenn man ein Dict hat, braucht man die Doku nicht mal anzusehen, es ist sofort klar.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Erstmal danke dass du dir Zeit genommen hast und danke für das Feedback :)
Leonidas hat geschrieben:So, nun zum aktuellen Benchmark-Code:

Zeile 1:

Code: Alles auswählen

#!/usr/bin/python
würde ich durch

Code: Alles auswählen

#!/usr/bin/env python
ersetzen. Ist universeller.
:? In jeden Sourcefile den ich gesehen habe steht das aber so. Selbst in den Libs von Python. Warum sollte ich das jetzt ändern? :?
Leonidas hat geschrieben: Zeile 83: Was ist denn das? Warum ist in

Code: Alles auswählen

BenchError (Exception)
nicht

Code: Alles auswählen

BenchmarkError(Exception)
(welches PEP8 konform und auch noch beschreibender wäre, du siehst, Exception-Namen haben kaum Abkürzungen: AttributeError statt AttrError oder E_ATTRERR).
Werde ich ändern. Danke für den Hinweis. Also gilt hier der Name des Moduls (oder Klasse??) + Error als postfix?

Leonidas hat geschrieben: Was macht die Klasse mehr als ein

Code: Alles auswählen

BenchmarkError(Exception): pass
? Ich habe das gefühl, dass es nur unnötiger Boilerplate-Code ist. Statt Error-Codes sollte man besser andere Exceptions definieren, denn Exceptions sind ja dafür da nichtssagende Error-Codes wie 1000 zu ersetzen.
Ein pass?? Ne beim besten willen nicht. Schau mal in Zeile 251-254. Dort wird eine Exception vom Type MB_ERR_GTRG_UNKNOWNMODE ausgelöst. Es bleibt jeden selber überlassen ob der die Exception abfängt und dann den Standard text ausgibt...

Code: Alles auswählen

try:
    […]
  data = bench.GetRanking("modus falsch")
except BenchError, err:
    print err
Oder ob er das so macht:

Code: Alles auswählen

try:
    […]
  data = bench.GetRanking(“modus falsch”)
except BenchError, err:
    if err.errorNum == MB_ERR_GTRG_UNKNOWNMODE:
       print "Der übergebene Modus an GetRanking() ist unbekannt!"
   if err.errNim == xyz # dies ist noch nicht implementiert. Es soll noch für eine Methode eine Exception implementiert werden.
       […]
Zweite Variante ist flexibler, weil man dann im try block alle Methoden verwenden kann von Benchmark und nicht in getrennten try blöcken (Zu viel code). Und falls irgend eine Methode eine Exception auslösest kann man mit if vergleichen welcher Fehler dafür gesorgt hat. Ein anderer Vorteil ist, das man eine Individuelle Fehlermeldung ausgeben kann.
Es gibt doch einige Exceptions die selber ein errno in der Exception Klasse definieren und das einen Error-Code enthält. Daher habe ich das ja. Die werden das ja aus den gleichen Grund haben oder :? Oder willst du mir jetzt sagen das ich lieber für jeden Fehler Typ, den eine Methode von Benchmark auslösen könnte, eine eigene exception definieren soll? :? Was ist den Pythonischer? Ich dachte ich könnte so eine Exception (in den Fall BenchErro bzw. BenchmarkError) definieren und müsste nicht für jeden Fehler ne eigene Klasse machen :?
Leonidas hat geschrieben: Zeile 108: Warum definierst du hier alles mit einem Bodenstrich? Unnötig und macht es schwerer lesbar.
Ich habe gelesen und auch BlackJack (in Pythons OOP erklärt) hat gesagt das man alle Methoden und alle Attribute die nicht Public sind, mit einen unterstrich am anfange definiert. Das finde ich sinnvoll weil keiner diese Sachen anfassen soll und ich die auch nicht dokumentieren werde, wie es schon in PEP8 vorgeschlagen wird. Also self._functions, self._data, self._refTime sind für Interna bestimmt (also private) und sollen von außen _nicht_ angerührt werden. Falls ich damit falsch liege und es nicht so ist dann bitte berichtigen. Aber in dem Fall bin ich mir 100% sicher das man so Private Sachen definiert und werde es auch so beibehalten.
Leonidas hat geschrieben: Zeile 140: nicht PEP8 kompatibel
Warum ist

Code: Alles auswählen

self._functions.append( [ref.__name__, comment, ref, args] )
Nicht PEP8 kompatibel :? Wegen den Leerzeichen? Sollte es so aussehen?:

Code: Alles auswählen

self._functions.append([ref.__name__, comment, ref, args])
Leonidas hat geschrieben: Zeile 154: self._data[0][0], [0][0] sieht sehr nichtssagend aus, vielleicht kann an hier eine andere Datenstruktur verwenden? Zum Beipiel ein Dict, mit einer Listen innen drin, oder sowas ähnliches?
Ok werde ich ändern, aber nicht als Dict mit listen drin, sonder als List mit Dicts drin, Weil...

Code: Alles auswählen

bench = Benchmark()
[...]
bench[0]['time'] # Laufzeit von Funktion 1. 
bench[1]['time'] # Laufzeit von Funktion 2, etc.
[...]
Dan kann man leicht darüber iterieren und braucht dann nur mit den Keys darauf zurückgreifen.
Leonidas hat geschrieben: Zeile 159: nicht PEP8 kompatibel

Zeile 168: dito
Ok, danke für den hinweis. Stimmt die Leerzeichen zwischen "-" und * und 80 Fehlt.
Leonidas hat geschrieben: Zeile 170: BM_GTRG_MODE_LIST finde ich völlig unnötig. Warum sollte man, wenn man schon die Möglichkeit hat, ein Dict zu bekommen, lieber eine undurchsichtige Struktur wie List haben wollen, bei der nicht sofort klar ist, welcher Index was für einen Wert repräsentiert? Klar, man kann Doku lesen, aber wenn man ein Dict hat, braucht man die Doku nicht mal anzusehen, es ist sofort klar.
Ganz einfach wegen den Beispiel in Zeile 286:

Code: Alles auswählen

print "\n--- Ranking mit GetRanking() als Array von Arrays holen und ausgeben ---"
    data = bench.GetRanking()
    print "Funktion: %2s - %-10s ist am langsamsten; Zeit: %f sec" %(
            data[0][2], data[0][1], data[0][0]
    )
    for time, comment, name, percent in data[1:]:
        print "Funktion: %2s - %-10s ist um %.2f%% schneller; Zeit: %f sec" % (
            name, comment, percent, time
        )
Das sieht übersichtlicher bzw. ist einfacher als Zeile 296:

Code: Alles auswählen

print "\n--- Ranking mit GetRanking() als Array von assoziative Arrays holen und ausgeben ---"
    data = bench.GetRanking(BM_GTRG_MODE_DICT)
    print "Funktion: %2s - %-10s ist am langsamsten; Zeit: %f sec" %(
            data[0]['name'], data[0]['comment'], data[0]['time']
    )
    for dat in data[1:]:
        print "Funktion: %2s - %-10s ist um %.2f%% schneller; Zeit: %f sec" % (
            dat['name'], dat['comment'], dat['percent'], dat['time']
        )
Aber ok, ich werde das ändern und dann mal zum abgleich (ohne docstrings. das kommt dann Später) hier mal posten ob die Änderungen dann so passen :)

Danke dir vielmals, hab heute viel gelernt :) Durch deine Hinweise weiß ich nun, das ich in viele scripte von mir richtig scheiße gebaut habe ^^

Aber wenn du Zeit hast könntest du ja die von mir kommentierten Sachen beantworten, weil ich mir da nicht sicher bin ob ich richtig liege.

thx & lg xtra

EDIT: Rechtschreibfehler verbessert.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

XtraNine hat geschrieben:Zeile 1:

Code: Alles auswählen

#!/usr/bin/python
würde ich durch

Code: Alles auswählen

#!/usr/bin/env python
ersetzen. Ist universeller.
:? In jeden Sourcefile den ich gesehen habe steht das aber so. Selbst in den Libs von Python. Warum sollte ich das jetzt ändern? :?[/quote]
Guck mal in Use /usr/bin/env python ... why? rein, dort werden die Möglichkeiten erklärt. Es ghet Grundsätzlich darum, dass nicht garantiert werden kann, wo der Python-Interpreter zu finden ist. Das Programm "env" sucht also den Python-Interpreter, findet ihn und startet ihn. Mit #!/usr/bin/python wird einfach nur in /usr/bin/python gesucht, und wenn er dort nicht ist, schlägts fehl. Und wo Python auf dem jeweiligen System zu finden ist, ist und kann nirgendwo bindend festgelegt werden.
Und guck mal hier, ich habe überall #!/usr/bin/env python verwendet
XtraNine hat geschrieben:Werde ich ändern. Danke für den Hinweis. Also gilt hier der Name des Moduls (oder Klasse??) + Error als postfix?
Nö, hier gilt einfach die Regel "Don't use unneccessary abreviations in exception names". Und Error-Codes sollte man, wie gesagt, auch nicht verwenden.
XtraNine hat geschrieben:Ein pass?? Ne beim besten willen nicht. Schau mal in Zeile 251-254. Dort wird eine Exception vom Type MB_ERR_GTRG_UNKNOWNMODE ausgelöst.
Ne, dort wird eine Exception vom Typ BenchError ausgelöst, die, um es noch seltsamer zu machen, noch auf den Errorcode geprüft werden muss.
XtraNine hat geschrieben:Es bleibt jeden selber überlassen ob der die Exception abfängt und dann den Standard text ausgibt...

Code: Alles auswählen

try:
    […]
  data = bench.GetRanking("modus falsch")
except BenchError, err:
    print err
Oder ob er das so macht:

Code: Alles auswählen

try:
    […]
  data = bench.GetRanking("modus falsch")
except BenchError, err:
    if err.errorNum == MB_ERR_GTRG_UNKNOWNMODE:
       print "Der übergebene Modus an GetRanking() ist unbekannt!"
   if err.errNim == xyz # dies ist noch nicht implementiert. Es soll noch für eine Methode eine Exception implementiert werden.
       […]
Nein, nein, das macht man anders:

Code: Alles auswählen

# definieren wir uns mal eine schöne Exception-Struktur
class BenchmarkError(Exception):
    pass

class UnknownModeError(BenchmarkError):
    pass

class ModeNotImplementedError(BenchmarkError):
    pass

# wie du siehst, tun die Exceptions nichts. Brauchen sie auch gar nicht
# ich habe noch eine Exception erfunden, damit ich demonstrieren kann
# wie man "Error-Codes" in Python implementiert

# nun wollen wir mal eine Fehlerbehandlungroutine einbauen:
try:
   data = bench.GetRanking(“modus falsch”)
except UnknownModeError:
   print "Der Modus ist unbekannt"
except: ModeNotImplementedError:
    print "Der Modus ist zwar vorhanden, aber noch nicht implementiert"
except BenchmarkError:
    print "Es ist ein nicht genauer spezifizierter Benchmark-Fehler passiert (für den es keine spezielle Fehlerbehandlung gibt"
except:
    print "Es ist ein absolut unerwarteter Fehler passiert"
XtraNine hat geschrieben:Zweite Variante ist flexibler, weil man dann im try block alle Methoden verwenden kann von Benchmark und nicht in getrennten try blöcken (Zu viel code).
Mein Code hat auch nur einen Try-Block und reagiert auf verschiedene Exceptions, ohne dass ich Fehlercodes auswerten muss.
XtraNine hat geschrieben:Und falls irgend eine Methode eine Exception auslösest kann man mit if vergleichen welcher Fehler dafür gesorgt hat. Ein anderer Vorteil ist, das man eine Individuelle Fehlermeldung ausgeben kann.
Kann ich so auch, spare mir sogar das Abfragen des Fehlercodes, weil Python das schon für mich macht. in GetResult muss ich nur eben die entsprechende Exception werfen, schon ist alles ok.
XtraNine hat geschrieben:Oder willst du mir jetzt sagen das ich lieber für jeden Fehler Typ, den eine Methode von Benchmark auslösen könnte, eine eigene exception definieren soll?
Ganz genau das. Fehlernummern machen nur sinn, wenn es wirklich viele viele verschiedene Exceptions geben kann, die nicht mehr überschaubar sind. Mit eigenen Exceptions hast du den Vorteil, dass du unglaublich fein Strukturieren kannst.
XtraNine hat geschrieben:Was ist den Pythonischer?
Schau dir deinen Code an, schau dir meinen Code an. Mein Exception-Handling ist übersichtlicher und kann mehr. Es kann sogar Exceptions abfangen, die es nicht kennt (durch die Oberkategorie BenchmarkException).
XtraNine hat geschrieben:Ich habe gelesen und auch BlackJack (in Pythons OOP erklärt) hat gesagt das man alle Methoden und alle Attribute die nicht Public sind, mit einen unterstrich am anfange definiert.
Stimmt, interne Funktionen und Variablen sind mit Unterstrich, "Daten" ohne. Aber da deine Klassen eigentlich nur "Daten" haben, macht das imho keinen Sinn (schwer zu beschreiben, aber einfach mal alles mit Unterstrich zu beginnen scheint mir schlicht umpythonisch, unter anderem weil dadurch nur unschöne Variablennamen rauskommen, ohne sonderlich überzeugenden Sinn).
XtraNine hat geschrieben:Das finde ich sinnvoll weil keiner diese Sachen anfassen soll und ich die auch nicht dokumentieren werde, wie es schon in PEP8 vorgeschlagen wird. Also self._functions, self._data, self._refTime sind für Interna bestimmt (also private) und sollen von außen _nicht_ angerührt werden. Falls ich damit falsch liege und es nicht so ist dann bitte berichtigen.
Das liegt in deinem Ermessen, du kannst es so oder so machen. Ich persönlich halte von sowas eigentlich nicht viel, weil mich die ganze Problematik (und Dogmatik) mit dem Private/Public total kalt lässt. Kann ich dir jetzt wirklich schwer erklären, bei mir ist es eben Einstellungssache. YMMV. Ich meine, wenn der Programmierer in der Klasse rumfischen will, ok, wenns auseinanderfällt ist er selbst schuld.
XtraNine hat geschrieben:Nicht PEP8 kompatibel :? Wegen den Leerzeichen? Sollte es so aussehen?:

Code: Alles auswählen

self._functions.append([ref.__name__, comment, ref, args])
Exakt.
XtraNine hat geschrieben:Ok werde ich ändern, aber nicht als Dict mit listen drin, sonder als List mit Dicts drin, Weil...

Code: Alles auswählen

bench = Benchmark()
[...]
bench[0]['time'] # Laufzeit von Funktion 1. 
bench[1]['time'] # Laufzeit von Funktion 2, etc.
[...]
Dan kann man leicht darüber iterieren und braucht dann nur mit den Keys darauf zurückgreifen.
Klar, ist gut.
XtraNine hat geschrieben:Das sieht übersichtlicher bzw. ist einfacher als Zeile 296:
Nein, ist es nicht. Man macht einfach eine Variable first, tail = data[0], data[1:] und arbeitet dann mit diesen Variablen weiter. Das ist schon wesentlich übersichlicher als [0][0]-Konstrukte.
XtraNine hat geschrieben:Aber wenn du Zeit hast könntest du ja die von mir kommentierten Sachen beantworten, weil ich mir da nicht sicher bin ob ich richtig liege.
Welche? Die hier? Habe ich hiermit also :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Wow! :shock:

Vielen Dank! :) Ich werde deine Vorschläge alle umsetzen! :) Außer das mit den _, das behalte ich bei, weil ich doch gerne damit signalisieren will, das die Attribute damit Privat sind. Ja ich weiß, ist ein dogam von C++ aber ist nun mal zu tief drin bei mir :) Außerdem, wenn ich dann mal wirklich ein Public Attribut habe, worauf man zugreifen muss, dann verliere ich selber die Übersicht ^^

Nochmals danke. Ich werde den Code morgen umschrieben. Und dabei spare ich dann noch mal docstrings und das ganze wird dann übersichtlicher.

Wegen den Markup in den Docstrings: Ich schaue mal ob ich das irgendwie anders lösen kann. Ich kucke mir mal noch andere doc-Werkzeuge an, vielleicht gibt es da bessere Möglichkeiten.


thx & lg xtra
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Zum Thema "private-Mechanismus" geht weiter unter: http://www.python-forum.de/topic-7790.html

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

benchmark.py v1.0.2 beta: http://lodgeit.lucumr.pocoo.org/show/79/

Bin fertig. Sollte jetzt alles den Richtlinien von PEP8 entsprechen.

GetRanking gibts nun nicht mehr. Hab das so gelöst: Nach dem in run() die Laufzeit untersucht wurde, wird danach das ranking erzeugt und in self.ranking gespeichert. Damit spare ich mir nen Gettert ;) self.ranking ist ein list von dicts => [{}, {}, ...]

print_ranking() gibt das ranking aus. Falls aber run() nicht ausgeführt wurde und gleich print_ranking() benutzt wird, wird eine Exception ausgelöst, die auf den Fehler hinweist. Ist mMn gut gelöst, weil stattdessen ein IndexError ausgelöst werden würde, da ja self.ranking ohne run() leer bleibt ;) benchmark.py ist aber gut dokumentiert und die main() wurde um alle möglichen Beispielen erweitert.

@Leo: Habe alles nach deinen Vorschlägen geändert bis auf folgendes:
Zeile 154: self._data[0][0], [0][0] sieht sehr nichtssagend aus, vielleicht kann an hier eine andere Datenstruktur verwenden? Zum Beipiel ein Dict, mit einer Listen innen drin, oder sowas ähnliches?
Hier kann ich nicht die Datenstruktur ändern. Das ganze basisiert darauf dass alles in die Liste gepusht wird, und danach die liste nach der Zeit (die sich jeweils auf Index 0 befindet) sortiert wird. Wenn ich nun ein dict machen würde dann würde es nur nach den Key sortiert und daher unbrauchbar. Hab das getestet und daher mache ich das so => [[], [], []]
Das spielt ja aber auch keine rolle, da es intern ist (und nur temporär) und nachher alles als list von dicts in self.ranking gespeichert wird => [{}, {}, …]. :)

lg
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

IMHO sind die eigenen Fehlerklassen unnötig!

Code: Alles auswählen

    def print_ranking(self):
        """Diese Methode zeigt das Ranking an stdout an."""
        if not self.ranking:
            raise NoValueError("Das Attribut ranking von %s enthaelt keine"
                               " Daten!\n" 
                               "Folgende Methode/Funktion hat die"
                               " Exception ausgeloest: %s"
                               % (self, self.print_ranking)
                              )
Warum nicht an der stelle einen normalen ValueError werfen? Dann kann man sich die eigenen Fehler-Klassen Zeile 39-51 komplett sparen. Ich würde es so machen:

Code: Alles auswählen

    def print_ranking(self):
        """Diese Methode zeigt das Ranking an stdout an."""
        if not self.ranking:
            raise ValueError(
                "Keine Daten vorhanden,"
                " da Benchmark noch nicht ausgeführt wurde!"
            )
Ein del(bench) vor einem bench = Benchmark() ist überflüssig. Zeile 207 und 218

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:


GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Hier die neue version: http://paste.pocoo.org/show/82/
diff: http://paste.pocoo.org/compare/81/
P.S.: Die docstring muss ich noch anpassen für die exceptions. Ging jetzt nicht auf die schnelle.

----

Ne, das sehe ich anders. Es geht ja gerade um die Differenzierung von Fehlern.

Hier ein Beispiel (Hab die Klasse erweitert):

angenommen wir übergeben statt einer Funktion eine Variable als Referenz…

Code: Alles auswählen

a = 0
bench = Benchmark()
bench.add_function(a, "LC", loops)
[…]
…dann kommt folgender traceback beim ausführen von add_function() der nichts sagend ist und wohlmöglich als Unterstellung genutzt werden könnte, das man keine richtige Fehlerüberprüfung implementiert hat (zu recht!)-->

Code: Alles auswählen

self._functions.append([ref.__name__, comment, ref, args])
AttributeError: 'int' object has no attribute '__name__'
So, nun implementiere ich in add_function eine Überprüfung:

Code: Alles auswählen

if str(type(ref)) != "<type 'function'>":
            raise WrongArgumentError("Das uebergeben Argument an ref ist " 
                                     "keine Referenz auf eine Funktion!")
Nun der gleiche aufruf:

Code: Alles auswählen

a = 0
bench = Benchmark()
bench.add_function(a, "LC", loops)
[…]
Traceback:

Code: Alles auswählen

  raise WrongArgumentError("Das uebergeben Argument an ref ist " 
__main__.WrongArgumentError: Das uebergeben Argument an ref ist keine Referenz auf eine Funktion!
Das ist schon viel aussagekräftiger :)

Genauso verhält es sich mit ValueError, das kein Bestandteil des Moduls ist. Warum so was benutzen, wenn es doch transparenter geht in dem ich dafür eine Eigene Exception definiere? Was passiert wenn irgendwas anderes im Try Block einen ValueError auslöst, das nichts mit meine Modul zu tun hat? Dann würde ich das wohl möglich abfangen und eine total falsche Fehlerbehandlung machen ;) Weist du was ich meine? Dan lieber eine eigen Exception die dafür sorgt das _nur_ die exceptions, im _pasenden_ except-block, abgefangen werden die von _meinen_ Modul ausgelöst werden.

Außerdem, geht es doch darum das man für jeden Fehler eine eigene passende Fehlerbehandlung macht. Das ist aber nicht möglich wen ich eine fremde Exception benutze, die mit meinem Modul nichts zu tun hat und dann auf einmal ne exception von irgendwo anders kommt und im gleichen except_block abgefangen werden, wo sie aber _nicht_ abgefangen werden dürfen! ;)

EDIT: im main() ist ein Beispiel.
Gesperrt