Ich möchte die derzeitige vorweihnachtliche Stille auf python-de stören und mal kundtun, was genau mich an Exceptions stört. Folgendes Beispiel: ------------- (snip here) ---------- def config(name, value): if cfg_open(): value = cfg_get(name, value) cfg_set(name, value) cfg_close() return value print config("Beispiel", 123) ------------- (snip here) ---------- Erklärungen dazu: - Ich möchte mit der Funktion einen benannten Konfigurationsparameter auslesen. - Der Parameter soll einen Default-Wert haben (den ich beim Aufruf mit angebe). - Der Parameter soll in der Konfiguration angelegt werden, wenn er noch nicht existiert - Wenn der angemeldete User keine Screibrechte auf der Konfiguration hat, so kann es doch gut sein, daß er Leserechte hat. - Wenn die Konfiguration nicht geöffnet werden kann, will ich den defaultwert haben. Das obige Beispiel läßt sich ungefähr so mit den Windows Registy-Funktionen leicht abbilden. Alle Funktionen liefern einen Fehlercode; schlägt QueryValue-Funktion (entspricht cfg_get() in obigem Beispiel), ändert sie den Parameterwert nicht; schlägt SetValue-Fehl, gibt es halt einen Fehlercode, usw. Obiger Pseudocode ist also nicht völlig realitätsfern. Bitte, wenn einer an dieser Stelle als Einwurf nur "Das Konzept ist kaputt" hat, ----> da is der Ausgang. [Ich habe das tatsächlich mit _winreg gemacht, aber hier im Beispiel Pseudofunktionen erwähnt, da mir bewusst ist, daß hier eine Reihe von nicht-Regianern mitliest] OK, meine Probleme mit dem ganzen: 1) Ich kann schlecht schreiben: try: cfg_open() value = cfg_get(name, value) cfg_set(name, value) cfg_close() except: # was tun ??? return value Weil, ich weiß ja nicht, welche der vier Funktionen eine Exception geworfen hat. Beispielsweise kann es ja sein, daß der cfg-Wert noch nicht existiert (cfg_get schmeisst eine Exception), und erst mit cfg_set angelegt wird. 2) Gut, ich könnte die Exception auswerten. Ich schreibe also: try: ... except exceptions.Exception, e: behandle(e) Das schlägt fehl, wenn einer irgendwo raise "irgendwas" stehen hat. Sprich, DER AUFRUFENDE CODE MUSS WISSEN, WAS DER AUFGERUFENE CODE FÜR EXCEPTIONS WERFEN KANN, egal wie tief dieser verschachtelt ist und was der seinerseits wieder für code anzieht. Ich weiss nicht, sehr nach OOP klingt das für mich nicht. Für mich ist die Lösung "Fehlercode über alles" immer noch anziehender. 3) Eine Lösung könnte so aussehen: try cfg_open() except: return value try: try: value = cfg_get(name, value) except: pass cfg_set(name, value) finally: cfg_close() return value Ich weiss nicht, ich weiss nicht, da sag noch einer, Exception Handling würde in einfacherem (und lesbarerem) Code resultieren... Abgesehen davon, daß ich hier noch gar keine Protokollfunktion eingebaut habe (In meinen C-Programmen einfach ein wegloggen des HRESULTs, hier müsste ein kompliziertes Auswerten der Exception stehen, inkl. der bei (2) angemerkten Einschränkungen. 4) Zusammenfassend: Mich stört a) daß ich wissen muß, dass UND WELCHE Exceptions eine Funktion wirft b) daß es nicht einfach ist, einzelne Funktionen "die Fehlschlagen dürfen" mit Funktionen "die in der Richtigen Reihenfolge aufgerufen werden müssen" zu mischen, wenn einem potentiell Exception Handling dazwischenfunkt. _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
DER AUFRUFENDE CODE MUSS WISSEN, WAS DER AUFGERUFENE CODE FÜR EXCEPTIONS WERFEN KANN,
Genau.
egal wie tief dieser verschachtelt ist und was der seinerseits wieder für code anzieht.
Falsch. Jede Funktion sollte dokumentieren, welche Ausnahmen sie *direkt* werfen kann. Wenn sie Funktionen ruft, die ihrerseits wieder Ausnahmen werfen können, muss sie diese Ausnahmen auch dokumentieren FALLS SIE ZUM NORMALEN BETRIEBSMODUS DER FUNKTION GEHÖREN (ich darf auch schreien). Wenn eine Funktion eine andere ruft und deren Ausnahmen nicht weiterreichen will, muss sie diese halt behandeln, indem die Ausnahmen beispielsweise in andere umgesetzt werden. Ausnahmen, die nicht zum normalen Betriebsmodus gehören (wie etwa AttributeError) sollte gar keiner behandeln - es handelt sich schlicht um Programmfehler.
Ich weiss nicht, sehr nach OOP klingt das für mich nicht. Für mich ist die Lösung "Fehlercode über alles" immer noch anziehender.
Ausnahmen bieten Dir alles, was Du mit Fehlercodes auch bekommst, und zusätzlich die Sicherheit, das Fehler, die im normalen Betrieb auftreten können, nicht ignoriert werden.
a) daß ich wissen muß, dass UND WELCHE Exceptions eine Funktion wirft
Wie unterscheidet sich das von Fehlercodes? Um zu wissen, ob eine Funktion erfolgreich war, musst Du auch die Dokumentation der Funktion lesen und verstehen, wie sie ihre Fehler mitteilt (Rückgabewert 0, Rückgabewert != 0, Rückgabewert < 0, Ausgabeparameter, usw.)
b) daß es nicht einfach ist, einzelne Funktionen "die Fehlschlagen dürfen" mit Funktionen "die in der Richtigen Reihenfolge aufgerufen werden müssen" zu mischen, wenn einem potentiell Exception Handling dazwischenfunkt.
Das Argument verstehe ich nicht. Welches Deiner Beispiele belegt es? Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Martin v. Löwis schrub am December 10, 2002 10:54 PM
Ausnahmen, die nicht zum normalen Betriebsmodus gehören (wie etwa AttributeError) sollte gar keiner behandeln - es handelt sich schlicht um Programmfehler.
Also, ich möchte das nicht persönlich formulieren, aber: das ist akademisch gedacht. Man kann in der Theorie sagen: Programmfehler sollen das Programm beenden, aber in der Praxis soll das verd*mmte Programm weiterlaufen, solange es geht. Stell dir vor, ein Bordcomputer im Flugzeug stürzt ab, weil irgendjemand in einer völlig unwichtigen Nebenfunktionen einen Fehlercode nicht abgefragt hat -> klar, das war ein Fehler, und der Fehler sollte bereinigt werden, und solange man in der Debugumgebung ist, ist es auch praktisch das assert == exception, aber wenn ich über dem Atlantik schwebe, dann soll bitteschön der Bordcomputer sich nicht einfach beenden, "damit ich den Fehler finden kann". Und wer behauptet, er habe in einer einigermaßen komplexen Umgebung sämtliche möglichen Situationen im Griff, der lügt. (Erste Stunde Informatikstudium, Programmieren 1: Jedes Program hat Fehler.) Anderes Beispiel: Wir programmieren Hardware. Es gibt Board-Revisionen. Wenn ich mich auf den Standpunkt stelle: da ist die Dokumentation, alles was um ein Haar davon abweicht, wird das in assert geklammert und das Programm beendet sich - habe ich sehr schnell sehr viele sehr stehende Maschinen draussen im Feld. Es kann sein, daß ich damit die Wirtschaft in Gestalt des Techniker-Serviceunternehmens ankurbel, aber mein Gehalt wird darunter leiden! (Es könnte den Seiteneffekt haben, daß endlich ein Gesetz gemacht wird, welches es IBM verbietet, eigene Hardwareprotokolle zu definieren, aber das ist eine andere Geschichte).
Ich weiss nicht, sehr nach OOP klingt das für mich nicht. Für mich ist die Lösung "Fehlercode über alles" immer noch anziehender.
Ausnahmen bieten Dir alles, was Du mit Fehlercodes auch bekommst, und zusätzlich die Sicherheit, das Fehler, die im normalen Betrieb auftreten können, nicht ignoriert werden.
Mein Punkt ist hier: manchmal macht es Sinn, gewisse Fehler zu ignorieren, und *da* muss ich dann ewigen Aufand betreiben.
a) daß ich wissen muß, dass UND WELCHE Exceptions eine Funktion wirft
Wie unterscheidet sich das von Fehlercodes? Um zu wissen, ob eine Funktion erfolgreich war, musst Du auch die Dokumentation der Funktion lesen und verstehen, wie sie ihre Fehler mitteilt (Rückgabewert 0, Rückgabewert != 0, Rückgabewert < 0, Ausgabeparameter, usw.)
Der Unterschied ist der Unterschied zwischen if open(): tuwas() else: tuwasanderes() und try: open() tuwas() except: tuwasanderes() Es tut mir leid, ich finde das obige Beispiel besser. Ich habe den Eindruck: Exception Handling ist ein Erziehungsmaßnahem für die Leute, die immer keine Rückgabewerte abgefragt haben. Fein! Solche Leute gibt es, und sie sollten erzogen werden. Aber ich *frage* meine Rückgabewerte ab, dann möchte ich sie bitte auch abfragen können und nicht mit in die Erziehungsmaßnahmen zwangsweise eingebunden werden. (Das ist ähnlich wie die Geschichte mit der Garbage Collection - es tut mir leid, mein letztes Speicherleak ist schon Jahre her, es handelte sich um 12 Bytes, die alle halbe Stunde dazukamen, und ja: ich überprüfe regelmäßig das Speicherverhalten meiner Programme, das geht mit einem billigen memorybuild wunderbar, und ja, die Programme sind in C++ und tatsächlich bei diversen Endkunden auf der Welt im Einsatz).
b) daß es nicht einfach ist, einzelne Funktionen "die Fehlschlagen dürfen" mit Funktionen "die in der Richtigen Reihenfolge aufgerufen werden müssen" zu mischen, wenn einem potentiell Exception Handling dazwischenfunkt.
Das Argument verstehe ich nicht. Welches Deiner Beispiele belegt es?
Das Beispiel 3? Bitte, sei ehrlich, die Lösung (die mit den vier Excepts, wo ich dann am Ende vielleicht auch noch die Exceptions einzeln in den vier Fällen auswerte) schaut Scheisse aus. Man kann ja Exceptions mögen oder nicht - der Code war Dreck. Punkt. Die "Fehlercodeversion" ist wie gesagt total simpel: - Wenn der Open nicht klappt, liefer ich den Default (egal, was das für ein Fehler ist) - Wenn der GetValue nicht klappt, wird die Variable nicht verändert - Wenn der SetValue nicht klappt, hat der Nutzer halt keine Rechte - Wenn der Close nicht klappt, ist es mir ehrlich gesagt egal das trace ich weg & aus is. Keiner der vier genannten Fehlersituationen ist es wert, ein "lebenswichtiges" (nicht wirklich) Programm einfach zu runterzufahren, blos weil ein Konfigparameter nicht existiert oder nicht geschrieben werden konnte. Die Konfiguration kann mich mal, wichtig ist daß das Programm an sich funktioniert. Nix für ungut, Gerson _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Vielleicht wird es in folgendem noch deutlicher: if open_resource_a(): if open_resource_b(): tuwas_mit_a_und_b() close_resource_b() else: fehlermeldung("nix is mit b") close_resource_a() else: fehlermeldung("nix is mit a") Die Logik dieses Codeauszugs ist doch wunderbar simpel. Jetzt du: schreib es mit exceptions. Hätte Python Destruktoren, und könnte man sich auf sie verlassen, wäre das close_resource_x() z.B. einfacher realisierbarer, aber so.... _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson.Kurz@t-online.de (Gerson Kurz) writes:
if open_resource_a(): if open_resource_b(): tuwas_mit_a_und_b() close_resource_b() else: fehlermeldung("nix is mit b") close_resource_a() else: fehlermeldung("nix is mit a")
Die Logik dieses Codeauszugs ist doch wunderbar simpel. Jetzt du: schreib es mit exceptions.
try: open_resource_a() except: fehlermeldung("nix is mit a") else: try: try: open_resource_b() except: fehlermeldung("nix is mit b") else: try: tuwas_mit_a_und_b() finally: close_resource_b() finally: close_resource_a() Die Logik finde ich simpler: Man kann die Fehlermeldungen den Problemen besser zuordnen. Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson Kurz wrote:
[...]
Ausnahmen bieten Dir alles, was Du mit Fehlercodes auch bekommst, und zusätzlich die Sicherheit, das Fehler, die im normalen Betrieb auftreten können, nicht ignoriert werden.
Mein Punkt ist hier: manchmal macht es Sinn, gewisse Fehler zu ignorieren, und *da* muss ich dann ewigen Aufand betreiben.
Du hast eine seltsame Definition von "ignorieren"! ;) Ich würde sagen, einen Fehler zu ignorieren heißt: "Ich will mich nicht um diesen Fehler kümmern, soll das doch wer anders machen." Was Du machen willst, ist, auf den Fehler zu reagieren, indem Du eine andere Funktionalität aufrufst, von der auch nur Du weißt, wie sie aussehen soll.
[...] Der Unterschied ist der Unterschied zwischen
if open(): tuwas() else: tuwasanderes()
und
try: open() tuwas() except: tuwasanderes()
Es tut mir leid, ich finde das obige Beispiel besser.
Mag ja sein, aber im Normalfall sieht Code wohl eher so aus: if open(): if tuwas(): if tunochwas(): return True else: return False else: return False else: return False und da finde ich open() tuwas() tunochwas() wesentlich übersichtlicher!
[...]
Bis demnächst, Walter Dörwald _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Walter Dörwald schreibt
Mag ja sein, aber im Normalfall sieht Code wohl eher so aus: if open(): if tuwas(): if tunochwas(): return True else: return False else: return False else: return False
und da finde ich
open() tuwas() tunochwas()
wesentlich übersichtlicher!
Du hast aber in deinem Beispiel vergessen, das in try/except zu klammern und die exceptions aufzudröseln... Nun gut, es ist eine Stilfrage, ich will mal nicht so sein. Aber, wo wir schon beim Kampf der praxisnahen Beispiele sind, hier kommt ein noch simpleres: if open(): tuwas() close() vs. try: open() try: tuwas() finally: close() except: pass Das scheinen mir zwei äquivalente Versionen zu sein, denn beachte: Close wird aufgerufen, auch wenn tuwas() fehlschlägt, aber nur wenn open() geklappt hat. _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson Kurz wrote:
Walter Dörwald schreibt
Mag ja sein, aber im Normalfall sieht Code wohl eher so aus: if open(): if tuwas(): if tunochwas(): return True else: return False else: return False else: return False
und da finde ich
open() tuwas() tunochwas()
wesentlich übersichtlicher!
Du hast aber in deinem Beispiel vergessen, das in try/except zu klammern und die exceptions aufzudröseln...
Eben nicht. Ich meinte, der überwiegende Teil normalen Programmcodes ignoriert Fehler.
Nun gut, es ist eine Stilfrage, ich will mal nicht so sein.
Aber, wo wir schon beim Kampf der praxisnahen Beispiele sind, hier kommt ein noch simpleres:
if open(): tuwas() close()
vs.
try: open() try: tuwas() finally: close() except: pass
KeyboardInterrupt und SystemExit abzufangen ist böse!
Das scheinen mir zwei äquivalente Versionen zu sein, denn beachte: Close wird aufgerufen, auch wenn tuwas() fehlschlägt, aber nur wenn open() geklappt hat.
Die beiden Versionen sind nur dann äquivalent, wenn Du wirklich alle Fehler unterdrücken willst. Angenommen Dein Code soll das Auftreten eines Fehlers nach oben weitermelden. Dann sehen die beiden Versionen folgendermaßen aus: res = open() if not res: res = tuwas() close() return res vs. open() try: tuwas() finally: close() Das sieht doch schon besser aus. Was ich damit sagen will ist folgendes: Im Normalfall überwiegt der Vorteil, daß Du Fehler komplett ignorieren (d.h. unbehandelt lassen und Deinem Aufrufer zur Behandlung überlassen) kannst, den Nachteil, daß Du ein if durch try/except/finally ersetzen mußt, bei weitem. Bis demnächst, Walter Dörwald _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson.Kurz@t-online.de (Gerson Kurz) writes:
Also, ich möchte das nicht persönlich formulieren, aber: das ist akademisch gedacht. Man kann in der Theorie sagen: Programmfehler sollen das Programm beenden, aber in der Praxis soll das verd*mmte Programm weiterlaufen, solange es geht.
Hängt vom Programm, würde ich sagen. Die Programme, die ich so schreibe, sollen in der Regel immer abbrechen, wenn sie nicht weiter wissen. Dabei handelt es sich um Compiler und andere Kommandozeilentools sowie cronjobs. Wenn ich einen Web- oder sonstigen Server schreibe, ist das was anderes: Der soll weiterlaufen. Allerdings soll er zuvor einen Logfile-Eintrag mit ner Fehlermeldung schreiben, und vielleicht noch irgend jemand benachrichtigen.
Stell dir vor, ein Bordcomputer im Flugzeug stürzt ab, weil irgendjemand in einer völlig unwichtigen Nebenfunktionen einen Fehlercode nicht abgefragt hat
Genau. "Den Fehler nicht behandeln" heißt ja nicht "sofort aufgeben". Die Ariane-Untersuchungskommission hat genau diesen Fehlschluss kritisiert, da eine unbehandelte Ausnahme den Computer in den Debugmodus versetzt hat (was dann von der Ariane als konfuse Steueranweisung verstanden wurde). Statt dessen muss man sich "ganz unten" wieder in den definierten Ausgangszustand bringen - irgendwas hat nicht geklappt, womit man nicht gerechnet hatte. Man muss jetzt davon ausgehen, dass das nicht zuende gekommen ist, aber eventuell schon angefangen hat. Was man dann macht, ist anwendungsspezifisch. Wenn da eine Transaktion ist, sollte man "rollback" auslösen. Wenn ein Subsystem gescheitert ist, sollte man es neu booten, oder (im Fall der Ariane) abschalten, weil die völlig unwichtige Nebenfunktion nicht mehr gebraucht wird. Das Gesamtsystem läuft in diesen Fällen natürlich weiter - wie auch in meinem Fall: Die Shell, in der ich Python ausgeführt habe, läuft weiter; auch PythonWin läuft weiter, obwohl ein Skript eine Ausnahme geworfen hat.
Ausnahmen bieten Dir alles, was Du mit Fehlercodes auch bekommst, und zusätzlich die Sicherheit, das Fehler, die im normalen Betrieb auftreten können, nicht ignoriert werden.
Mein Punkt ist hier: manchmal macht es Sinn, gewisse Fehler zu ignorieren, und *da* muss ich dann ewigen Aufand betreiben.
Da machst Du das ignorieren falsch. Unerwartete Fehler sollten grobkörnig ignoriert werden, nicht feinkörnig. Damit kann man während des Entwickelns gewisse Klassen unerwarteter Fehler finden und eventuelle Bugs fixen, und dann mit geringem Aufwand eine Version produzieren, die auch unerwartete Fehler ignoriert.
Ich habe den Eindruck: Exception Handling ist ein Erziehungsmaßnahem für die Leute, die immer keine Rückgabewerte abgefragt haben. Fein! Solche Leute gibt es, und sie sollten erzogen werden. Aber ich *frage* meine Rückgabewerte ab, dann möchte ich sie bitte auch abfragen können und nicht mit in die Erziehungsmaßnahmen zwangsweise eingebunden werden.
Das Problem ist nun, dass die Funktionen keine Rückgabecodes liefern, die man abfragen könnte. Das ist aber leicht zu beheben: def err_open(*args): try: return open(*args) except: return None Dann kannst Du err_open benutzen, und den Fehlercode abfragen. Das geht auch ein bisschen allgemeiner: def err(func, *args): try: return func(*args) except: return None Dann schreibt mann f = err(open, "/etc/passwd", "w")
Das Argument verstehe ich nicht. Welches Deiner Beispiele belegt es?
Das Beispiel 3? Bitte, sei ehrlich, die Lösung (die mit den vier Excepts, wo ich dann am Ende vielleicht auch noch die Exceptions einzeln in den vier Fällen auswerte) schaut Scheisse aus. Man kann ja Exceptions mögen oder nicht - der Code war Dreck. Punkt.
Wie wäre es mit try: cfg_open() value = cfg_get(name, value) # Was bedeutet hier eigentlich der # value-Parameter? except CfgCouldNotOpen: return value except CfgKeyError: try: cfg_set(name, value) except CfgPermissionDenied: pass cfg_close() return value Das liest sich ungefähr so: - Öffne, lese - Wenn Öffnen nicht geht, Defaultwert - Wenn Lesen nicht geht, Schreiben - Wenn Schreiben nicht geht, ignorieren - Schließen, fertig Dieses Programm behandelt die erwarteten Fehler; nicht erwartete Ausnahmen (etwa eine Meldung vom UPS über bevorstehenden Stromausfall) werden nach außen weitergereicht. Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Martin v. Löwis schreibt:
Da machst Du das ignorieren falsch. Unerwartete Fehler sollten grobkörnig ignoriert werden, nicht feinkörnig. Damit kann man während des Entwickelns gewisse Klassen unerwarteter Fehler finden und eventuelle Bugs fixen, und dann mit geringem Aufwand eine Version produzieren, die auch unerwartete Fehler ignoriert.
Vielleicht liegt mein Problem darin, daß ich immer an das Modell denke: Mehrere Programmteile (unterschiedlicher Firmen) arbeiten zusammen. So ist das z.B. in unserem Umfeld - wir schreiben Gerätesteuerungen, andere setzen diese ein. Ich möchte mein API so gerade wie möglich auf *meiner* Ebene halten, weil ich aus leidgeprüfter Erfahrung weiß, was eine "Problemeskalation durch den Vorstand" ist.
Dann kannst Du err_open benutzen, und den Fehlercode abfragen. Das geht auch ein bisschen allgemeiner:
def err(func, *args): try: return func(*args) except: return None
Du wirst lachen - sowas ähnliches habe ich auch (heisst bei mir "always"), nur gebe ich ein tupel (result, errorcode) zurück, weil ich nicht wissen kann, ob "None" ein gültiger Rückgabewert oder der Fehler ist. Daß es den Code nicht schöner macht, brauche ich nicht zu erklären.
Wie wäre es mit
try: cfg_open() value = cfg_get(name, value) # Was bedeutet hier eigentlich der # value-Parameter? except CfgCouldNotOpen: return value except CfgKeyError: try: cfg_set(name, value) except CfgPermissionDenied: pass
cfg_close() return value
Das ist zugegeben lesbarer, mich würde jetzt nur noch stören, daß ich nur explizit diese beiden Exceptions wissen muss. (Ich musste übrigens den Code zweimal lesen, um zu erkennen, daß cfg_close() nicht aufgerufen wird, wenn cfg_open() fehlschlägt). _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson.Kurz@t-online.de (Gerson Kurz) writes:
try: cfg_open() value = cfg_get(name, value) # Was bedeutet hier eigentlich der # value-Parameter? except CfgCouldNotOpen: return value [...]
Das ist zugegeben lesbarer, mich würde jetzt nur noch stören, daß ich nur explizit diese beiden Exceptions wissen muss.
Aber das ist doch _haargenau_ das gleiche wie bei Fehlercodes, die muss der Rufer doch auch kennen. Die Ausnahmen einer Operation gehören zum Interface dieser Operation.
(Ich musste übrigens den Code zweimal lesen, um zu erkennen, daß cfg_close() nicht aufgerufen wird, wenn cfg_open() fehlschlägt).
Ich denke, das ist eine Übungsfrage. Durch das Except-Else-Konstrukt kann man Programme so schreiben, dass die Fehler in Leserichtung zuerst behandelt werden, während sie aufgrund der Gewohnheit an anderer Stelle erwartest. Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Kleiner Nachtrag zu:
value = cfg_get(name, value) # Was bedeutet hier eigentlich der # value-Parameter?
cfg_get soll den Defaultwert (value) zurückliefern, wenn es den Parameter nicht gibt. Unter C hätte ich einen Pointer, den ich nicht verändere, und meinen geliebten Errorcode. _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson.Kurz@t-online.de (Gerson Kurz) writes:
cfg_get soll den Defaultwert (value) zurückliefern, wenn es den Parameter nicht gibt. Unter C hätte ich einen Pointer, den ich nicht verändere, und meinen geliebten Errorcode.
Warum kann dann cfg_get überhaupt noch scheitern? Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Martin v. Löwis frägt
cfg_get soll den Defaultwert (value) zurückliefern, wenn es den Parameter nicht gibt. Unter C hätte ich einen Pointer, den ich nicht verändere, und meinen geliebten Errorcode.
Warum kann dann cfg_get überhaupt noch scheitern?
Weil ich z.B. Tracen möchte, was genau fehlgeschlagen ist (etwa: Wert existiert nicht in der Konfiguration, Wert existiert in der Konfiguration, hat aber einen anderen Datentyp usw.) Und angenommen, es handelt sich nicht um die gerngehasste Registry, sondern um ein Api eines Drittanbieters: es kann ja gut sein, daß dessen cfg_get() einen Trace schreibt, aber ich möchte in meinem Trace auch sehen, was genau schiefgelaufen ist. Ursache für das Beispiel war natürlich, daß _winreg.QueryValue() eine Exception wirft, wie mir (glücklicherweise frühzeitig) schmerzlich bewusst wurde. Mir ist übrigens noch ein Grund gegen Exceptions eingefallen: Interoperabilität. Exceptions sind sprachgebunden (häufig sogar compilerabhängig) - fang mal in C eine exception aus einer C++-Library. _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson.Kurz@t-online.de (Gerson Kurz) writes:
Mir ist übrigens noch ein Grund gegen Exceptions eingefallen: Interoperabilität. Exceptions sind sprachgebunden (häufig sogar compilerabhängig) - fang mal in C eine exception aus einer C++-Library.
Das hat nun mit Python relativ wenig zu tun; trotzdem: Auch Fehlercodes sind sprachgebunden. Werte mal einen Java-Fehlercode in Fortran aus. Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson Kurz wrote:
a) daß ich wissen muß, dass UND WELCHE Exceptions eine Funktion wirft
Alles was du brauchst ist die Erkenntnis, dass jede Exception von der Klasse "Exception" abgeleitet ist. Den tatsaechlichen Klassennamen einer konkreten Instanz brauchst du nur dann zu wissen, wenn du darauf ganz spezifisch eingehen willst.
b) daß es nicht einfach ist, einzelne Funktionen "die Fehlschlagen dürfen" mit Funktionen "die in der Richtigen Reihenfolge aufgerufen werden müssen" zu mischen, wenn einem potentiell Exception Handling dazwischenfunkt.
Findest du wirklich, dass folgendes komplizierter ist, als was du in C mit den HRESULTs machst? try cfg_open() except Exception, e: log('Oeffnen der Konfiguration fehlgeschlagen. (%s)' % str(e)) return value try: try: value = cfg_get(name, value) except Exception, e: log('Lesen von "%(k)s" fehlgeschlagen. (%(e)s)' % {'k':name, 'e':str(e)}) try: cfg_set(name, value) except Exception, e: log('Schreiben von "%(k)s:%(v)s" fehlgeschlagen. (%(e)s)' % {'k':name, 'v':value, 'e':str(e)}) finally: cfg_close() return value Gegenueber den HRESULTs hat das sogar noch den Vorteil, dass in deinem Log dann nicht nur steht: "Fehler: 43948", sondern etwas einigermassen aussagekraeftiges (naja, wie z.B. "Der Parameter ist falsch", oder was immer den Jungs von MS wieder kreatives eingefallen ist). -schorsch -- Georg Mischler -- simulations developer -- schorsch at schorsch com +schorsch.com+ -- lighting design tools -- http://www.schorsch.com/ _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
On Tue, 10 Dec 2002 17:04:48 -0500 (EST), Georg Mischler wrote:
try cfg_open() except Exception, e: log('Oeffnen der Konfiguration fehlgeschlagen. (%s)' % str(e)) return value
Wie schon der Code des orginalen Anfragers krankt auch dieser Code daran, daß hier wahllos Exceptions gefressen werden. In der Annahme, daß cfg_open auf dem Dateissystem arbeit, geht's so besser: try: cfg_open() except EnvironmentError, e: if e.errno == errno.ENOENT: # file not found else: raise # other errors Man kann auf die Verfeinerung mit errno verzichten, entscheidend ist, daß hier nicht Dinge wie Syntaxfehler etc. gnadenlos mit abgefangen werden. Ciao, Jürgen _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
In der Annahme, daß cfg_open auf dem Dateissystem arbeit, geht's so besser:
Du hast das Stichwort gesagt: In der *Annahme*. Angenommen, ich habe eine Abstrakte Konfigurationsdatenbankklasse und weiß jetzt noch nicht, ob später jemand da mal eine Registry hinpflanzt, oder eine Datenbank, oder ein Kommunikationsmodul mit der Totenwelt. Abgesehen davon wird mein code - denn ich jetzt schon mit "Sieht Scheisse aus" qualifizieren möchte - dadurch nicht hübscher. _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
Gerson.Kurz@t-online.de (Gerson Kurz) writes:
Abstrakte Konfigurationsdatenbankklasse und weiß jetzt noch nicht, ob später jemand da mal eine Registry hinpflanzt, oder eine Datenbank, oder ein Kommunikationsmodul mit der Totenwelt.
Dann solltest Du für diese abstrakte Klasse eine abstrakte Ausnahme definieren, die konkrete Ausnahme abfangen und statt derer die abstrakte weiterleiten. Wenn sich dann irgend wann mal was irgend wohin pflanzt, musst Du die neue konkrete Ausnahme, die dort sprießt, abschneiden und umptopfen. Ciao, Martin _______________________________________________ Python-de maillist - Python-de@starship.python.net http://starship.python.net/mailman/listinfo/python-de
participants (6)
-
Georg Mischler -
Gerson.Kurz@t-online.de -
Juergen Hermann -
Martin v. Löwis -
martin@v.loewis.de -
Walter Dörwald