Fehlerausgabe bei eigenen Modulen/Bibliotheken
Grüße an die Python-Gemeinschaft, ich entwickle zur Zeit ein kleines Modul, vorerst für den privaten Gebrauch; wenn es ausgereift ist, habe ich vor dieses eventuell auch zu veröffentlichen. Mich stellt sich aber momentan eine Frage. Wie kann ich in Python professionell eine Fehlerausgabe implementieren. D.h. wenn ein Programmierer in seinem Projekt dieses Modul einbindet und dabei zum Beispiel falsche Parameter an Modulfunktionen übergibt. Dann soll noch währender der Interpretierung und/oder der Ausführung seines Quellcodes das Programm stoppen und eine Fehlermeldung ausgegeben werden. In der Form: File "dateiXY", line XY, in ModulXY TypeError: Funktion_mit_falschen_Parametern(): received integer, tupel required Mit anderen Worten eine Typenprüfung. Als weiteres Beispiel fällt mir noch ein, wenn die Werteanzahl einer Liste oder eines Tupel zu gering/groß ist. Auch hier sollte entsprechend eine Fehlermeldung erscheinen. Diese Beispiele sollen nur verdeutlichen, was ich meine. Mir geht es also um eine allgemeine Herangehensweise des Problems. Gibt es dafür eine Lösung in Python? Danke! Mit freundlichen Grüßen, Johannes Markert
Wenn Nutzer irgendwas übergibt was nicht passt wird eine Exception auftreten und der Nutzer schaut in die Dokumentation deines Moduls, was er eigentlich auch hätte gleich machen können.
Hallo Johannes,
Wie kann ich in Python professionell eine Fehlerausgabe implementieren. D.h. wenn ein Programmierer in seinem Projekt dieses Modul einbindet und dabei zum Beispiel falsche Parameter an Modulfunktionen übergibt.
Es geht Dir also um Programmierfehler. Das ist ein wichtiger Punkt, denn Programmierern kann (und muss) man Fehler ganz anders melden als Anwendern. Und Programmierfehler melden ist ganz einfach, indem Du eine Exception auslöst (Befehl "raise"). Unten ein Beispiel, wie Du (nach der Lehre der Programmiersprache Eiffel) die Vorbedingungen prüfst.
Dann soll noch währender der Interpretierung und/oder der Ausführung seines Quellcodes das Programm stoppen und eine Fehlermeldung ausgegeben werden.
In der Form:
File "dateiXY", line XY, in ModulXY TypeError: Funktion_mit_falschen_Parametern(): received integer, tupel required
Ungefähr das gibt der Python-Interpreter aus, wenn eine Exception ausgelöst wird, die nicht abgefangen wird. Mehr dazu findest Du hier: http://docs.python.org/tutorial/errors.html Und nun das Beispiel: def ich_teste_selbst(zahl, elemente): assert isintance(zahl, int) assert len(elemente) = 4, "'elemente' muss 4 Elemente haben" Die Anweisung "assert" ist dabei nicht anderes als if not <bedingung>: raise AssertionError siehe auch http://docs.python.org/reference/simple_stmts.html#the-assert-statement. Allerdings werden solche Prüfungen in Python eher selten. Denn ein Programmierfehler kommt auch zum Vorschein in diesem Beispiel: def ich_lasse_testen(zahl, elemente): zahl = zahl / 5 # erlaubt auch int, long, float host, port, user, password = elemente # müsen 4 Elemente sein Wenn Du das aufrufst:
ich_lasse_testen('Hallo', (1,2,3,4)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in ich_lasse_testen TypeError: unsupported operand type(s) for /: 'str' and 'int'
(Wobei man im letzteren Fall vielleicht besser ein dict oder eine einfache Klasse verwenden würde.) -- Schönen Gruß - Regards Hartmut Goebel Dipl.-Informatiker (univ.), CISSP, CSSLP Goebel Consult Spezialist für IT-Sicherheit in komplexen Umgebungen http://www.goebel-consult.de Monatliche Kolumne: http://www.cissp-gefluester.de/ Goebel Consult mit Mitglied bei http://www.7-it.de
Hallo Johannes, On 2010-08-05 00:11, Johannes Markert wrote:
Grüße an die Python-Gemeinschaft,
Grüße zurück! ;-)
ich entwickle zur Zeit ein kleines Modul, vorerst für den privaten Gebrauch; wenn es ausgereift ist, habe ich vor dieses eventuell auch zu veröffentlichen.
Mich stellt sich aber momentan eine Frage. Wie kann ich in Python professionell eine Fehlerausgabe implementieren. D.h. wenn ein Programmierer in seinem Projekt dieses Modul einbindet und dabei zum Beispiel falsche Parameter an Modulfunktionen übergibt. [...]
Man unterscheidet üblicherweise zwischen unvorhersehbaren Fehlern, zum Beispiel einer fehlenden Datei, und Programmierfehlern. Zu letzteren würde auch ein falscher Aufruf deiner Modulfunktion zählen. Die unvorhersehbaren Fehler werden üblicherweise behandelt, indem man eine "High-Level"-Ausnahme wirft. Was man eher nicht machen sollte, ist, einen IndexError oder KeyError einfach "durchzulassen". Das hätte den Nachteil, dass der Aufrufer, wenn du mal einen Container in der Implementierung deines Moduls von einer Liste auf ein Dictionary änderst, plötzlich eine andere Ausnahme bekommt. Nun zu deiner eigentlichen Frage. :-) Programmierfehler werden im Allgemeinen in Python gar nicht behandelt, da man davon ausgeht, dass ein Benutzer deiner Bibliothek ohnehin testen muss, auch um logische Fehler zu finden. Wenn eine Funktion zum Beispiel sinus heißt, aber den Cosinus des Arguments zurückgibt, nützt der korrekte Aufruf mit einem Float-Parameter auch nichts. ;-) Python als Sprache verzichtet bewusst auf Typprüfungen, deshalb ist es auch nicht sinnvoll, das nachzubilden. Du kannst aber, quasi als "ausführbare Dokumentation", assert-Anweisungen verwenden. Das würde ich aber nur tun, wenn du bestimmte Fehler regelrecht erwartest; routinemäßig alle denkbaren falschen Aufrufe mit assert absichern würde ich nicht. Ganz konkrete Typen zu verlangen, schränkt außerdem die Aufrufmöglichkeiten oft unnötig ein. Die allermeisten Funktionen, die ein Tupel vertragen, laufen zum Beispiel auch mit einer Liste. _Falls_ du eine Typprüfung vornehmen willst, verwende isinstance statt mit einem ganz konkreten Typ zu vergleichen, denn isinstance(obj, Class) gibt auch einen wahren Wert zurück, wenn obj zu einer von Class abgeleiteten Klasse gehört.
Dann soll noch währender der Interpretierung und/oder der Ausführung seines Quellcodes das Programm stoppen und eine Fehlermeldung ausgegeben werden.
Das wäre mit assert-Anweisungen gegeben. Aber auch die meisten anderen Aufruf-Probleme machen sich schnell durch AttributeError, IndexError etc. bemerkbar.
In der Form:
File "dateiXY", line XY, in ModulXY TypeError: Funktion_mit_falschen_Parametern(): received integer, tupel required
Mit anderen Worten eine Typenprüfung.
Siehe oben.
Als weiteres Beispiel fällt mir noch ein, wenn die Werteanzahl einer Liste oder eines Tupel zu gering/groß ist. Auch hier sollte entsprechend eine Fehlermeldung erscheinen.
Auch das solltest du, falls es in die Kategorie Programmierfehler fällt, mit assert behandeln. Ein fehlgeschlagenes assert löst einen AssertionError aus. Wenn du es "nicht lassen kannst" eine Typ-Prüfung zu implementieren, kannst du eine ProgrammingError-Exception definieren und diese im Fehlerfall mit möglichst ausführlichen Informationen auslösen. Eine scheinbare Parallele gibt es bei der Python-Datenbank-API, bei der ein ProgrammingError ausgelöst werden kann. Diese Exception bezieht sich aber eher auf Fehler, die in der Abstimmung von Programm und Datenbank liegen, zum Beispiel wenn gegen ein Constraint wie NOT NULL oder UNIQUE verstoßen wird. Diese Dinge sind ebenso wie auch externe Daten nicht vorhersehbar, fallen also wohl nicht in die gleiche Kategorie wie "offensichtlich" fehlerhafte Aufrufe. Um das ganze Kopfzerbrechen um falsche Aufrufe möglichst weitgehend zu vermeiden, solltest du von vornherein auf eine für deine Modul-Funktionalität möglichst "natürliche" API achten. Ich könnte mir vorstellen, dass du bei deiner Frage schon bestimmte potenziell fehleranfällige Schnittstellen im Sinn hattest. In dem Fall frag ruhig konkret nach Rat für die Gestaltung der Aufruf-Schnittstelle. Viele Grüße Stefan
Hallo Johannes, falls du es noch nicht gemacht hast, schau dir mal die Module an, welche Python mitbringt. Man lernt viel durch das Studium von anderen. Grüße Mike
Wow, ich bin echt beeindruckt von den vielen ausführlichen Antworten. Sie haben mir wirklich sinnvolle Denkanstöße gegeben. Ich danke dafür herzlich. Und weil das so super geklappt hat, schließe ich gleich eine weitere Frage an: ;-) Nach euren Erfahrungen ... Was ist besser: Die Fehlerbehandlung gleich während der Planung des Programms mit zu berücksichtigen und im Quelltext zu verankern oder nach der Grundimplementation diese nachträglich einzubauen? Gruß, Johannes
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Johannes Markert wrote:
Wow,
ich bin echt beeindruckt von den vielen ausführlichen Antworten. Sie haben mir wirklich sinnvolle Denkanstöße gegeben. Ich danke dafür herzlich.
Und weil das so super geklappt hat, schließe ich gleich eine weitere Frage an: ;-)
Nach euren Erfahrungen ... Was ist besser: Die Fehlerbehandlung gleich während der Planung des Programms mit zu berücksichtigen und im Quelltext zu verankern oder nach der Grundimplementation diese nachträglich einzubauen?
Da gibt es sicherlich keine goldenen Regel. Bestimmte Checks wird man sicherlich schon von vornerein einbauen - andere erst später, wenn man rausfindet, dass man seinen Code robuster machen muss. - -aj -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (Darwin) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iEYEARECAAYFAkxa0rUACgkQCJIWIbr9KYzX8QCaA9etYF/fpPCMO+8YzACzV/LY eYIAnjK7VDP9SBLyPOLAGug7ahTDQceO =/+2H -----END PGP SIGNATURE-----
Hallo, ganz klar während der Entwicklung. Anders ist das ja auch gar nicht wirklich möglich, den wenn du Fehler behandelst kommt ja ein ganz anderer Programmfluss zustande als ohne. Anders ist es natürlich wenn du erst Prototyping betreibst, dann kannst du auf die Fehlerbehandlung verzichten, solltest aber dann auch bei der eigentlichen Implementierung von Grund auf neu ansetzten. Gruß Sebastian Am 05.08.2010 um 16:38 schrieb Johannes Markert:
Wow,
ich bin echt beeindruckt von den vielen ausführlichen Antworten. Sie haben mir wirklich sinnvolle Denkanstöße gegeben. Ich danke dafür herzlich.
Und weil das so super geklappt hat, schließe ich gleich eine weitere Frage an: ;-)
Nach euren Erfahrungen ... Was ist besser: Die Fehlerbehandlung gleich während der Planung des Programms mit zu berücksichtigen und im Quelltext zu verankern oder nach der Grundimplementation diese nachträglich einzubauen?
Gruß, Johannes
_______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Hallo Johannes, On 2010-08-05 16:38, Johannes Markert wrote:
ich bin echt beeindruckt von den vielen ausführlichen Antworten. Sie haben mir wirklich sinnvolle Denkanstöße gegeben. Ich danke dafür herzlich.
bitteschön. :)
Und weil das so super geklappt hat, schließe ich gleich eine weitere Frage an: ;-)
Nach euren Erfahrungen ... Was ist besser: Die Fehlerbehandlung gleich während der Planung des Programms mit zu berücksichtigen und im Quelltext zu verankern oder nach der Grundimplementation diese nachträglich einzubauen?
Ich würde folgendermaßen vorgehen: 1. Je nach Erfahrung mit OOP und Umfang/Komplexität der Software das OO-Design ggf. vor dem Schreiben des Codes überlegen. Dabei auch schon an mögliche Fehlerbedingungen denken. 2. Bevor du irgendwelchen Produktionscode schreibst, schreib etwas Code, wie er den Produktionscode benutzen würde. Spiel ein bisschen mit der API rum, die du dir überlegt hast. Schau dir an, ob sich dein Code in einfachen, komplizierten und Fehler-Fällen "angenehm anfühlt". Heb diesen Code auf; evtl. kannst du ihn noch für automatisierte Tests verwenden. 3. Abwechselnd Testcode und Produktionscode schreiben (erst die Tests; "test-driven development"). Wenn ich mir noch gar nicht sicher bin, ob sich etwas so wie ich mir vorstelle, überhaupt implementieren lässt, schreibe ich auch manchmal den Produktionscode zuerst. 4. Wenn sich später herausstellt, dass es irgendwo hakt, also sich die API kompliziert anfühlt, die Probleme möglichst früh beheben bzw. refaktorieren. Hier helfen wieder die automatisierten Tests. Viele Grüße Stefan
Am 05.08.2010 20:30, schrieb Stefan Schwarzer:
Ich würde folgendermaßen vorgehen:
Schöne Beschreibung. Besonders das mit dem "sich die API kompliziert anfühlt" :-) Setzt allerdings Erfahrung voraus. -- Schönen Gruß - Regards Hartmut Goebel Dipl.-Informatiker (univ.), CISSP, CSSLP Goebel Consult Spezialist für IT-Sicherheit in komplexen Umgebungen http://www.goebel-consult.de Monatliche Kolumne: http://www.cissp-gefluester.de/ Goebel Consult mit Mitglied bei http://www.7-it.de
Hallo, für mich als Python Neuling hat sich genau die selbe Frage auch gestellt. Deshalb möchte ich mal meine These vorstellen, warum es "pythonisch" ist, keine Type Checks zu machen ;-) Ich denke nämlich das Python hier gar nicht aus dem Raster fällt. Wenn man mich Fragen würde, ob ich sonst immer Type Checks gemacht habe, dann würde ich sofort sagen "Ja, immer". In Wirklichkeit habe ich es aber gar nicht wirklich gemacht, sondern ich habe auf Interfaces und Abstrakte Klassen gecheckt. Durch diese Möglichkeit konnte ich flexible Architekturen entwerfen, ohne dabei auf Checks verzichten zu müssen. Nun ist in Python alles etwas flexibler und Interfaces und abstrakte Klassen gibt es nicht (oder ich habe sie zumindest so direkt noch nicht gefunden). Daher muss man eben mehr auf die Vernunft des Clients bauen. Allerdings nun auf einzelne Klassen zu prüfen wäre der falsche Weg, in anderen Sprachen würde man es ja auch nicht machen. Mein Wort zum Donnerstag ;-) Seht ihr das auch so, oder was ist eure Meinung dazu? Gruß Sebastian Am 05.08.2010 um 00:11 schrieb Johannes Markert:
Grüße an die Python-Gemeinschaft,
ich entwickle zur Zeit ein kleines Modul, vorerst für den privaten Gebrauch; wenn es ausgereift ist, habe ich vor dieses eventuell auch zu veröffentlichen.
Mich stellt sich aber momentan eine Frage. Wie kann ich in Python professionell eine Fehlerausgabe implementieren. D.h. wenn ein Programmierer in seinem Projekt dieses Modul einbindet und dabei zum Beispiel falsche Parameter an Modulfunktionen übergibt. Dann soll noch währender der Interpretierung und/oder der Ausführung seines Quellcodes das Programm stoppen und eine Fehlermeldung ausgegeben werden.
In der Form:
File "dateiXY", line XY, in ModulXY TypeError: Funktion_mit_falschen_Parametern(): received integer, tupel required
Mit anderen Worten eine Typenprüfung.
Als weiteres Beispiel fällt mir noch ein, wenn die Werteanzahl einer Liste oder eines Tupel zu gering/groß ist. Auch hier sollte entsprechend eine Fehlermeldung erscheinen.
Diese Beispiele sollen nur verdeutlichen, was ich meine. Mir geht es also um eine allgemeine Herangehensweise des Problems. Gibt es dafür eine Lösung in Python?
Danke!
Mit freundlichen Grüßen, Johannes Markert
_______________________________________________ python-de maillist - python-de@python.net http://python.net/mailman/listinfo/python-de
Hallo, Am 05.08.2010 22:10, schrieb Sebastian Bechtel:
für mich als Python Neuling hat sich genau die selbe Frage auch gestellt. Deshalb möchte ich mal meine These vorstellen, warum es "pythonisch" ist, keine Type Checks zu machen ;-) [...] Nun ist in Python alles etwas flexibler und Interfaces und abstrakte Klassen gibt es nicht (oder ich habe sie zumindest so direkt noch nicht gefunden). [...] Allerdings nun auf einzelne Klassen zu prüfen wäre der falsche Weg, in anderen Sprachen würde man es ja auch nicht machen.
Die Typechecks muss man ja auch nicht selbst machen, das macht ja Python für einen, wenn es nötig ist. Anderer Programmiersprachen haben "Interfaces" und man kann/muss testen, ob einen Klasse dieses Interface implementiert. In Python spart man sich das und macht statt dessen "duck-typing" http://de.wikipedia.org/wiki/Duck-Typing. Das funktioniert grob so: Wenn ich ein Element an ein "Liste"-Objekt anhängen will, also eigentlich Elemente sammeln will, dann muss die "Liste" dafür ja nur die Methode "append()" unterstützen. Wenn ein Objekt übergeben wird, das append nicht kann, dann fällt das beim Testen auf. Die Philosophie ist die: Warum bei 100% der Objekte prüfen, ob sie eine bestimmte Klasse haben oder ein bestimmtes Interface implementieren, wenn doe (negative) Prüfung nur bei 0,0001% relevant ist? Da ist es billiger (aka schneller) im Fehlerfall zu schreien. [Bei einer statisch typisierten Sprach wie Java sind solche Tests eher sinnvoll. Dann da kann der Compiler zur Übersetzungszeit prüfen.] Abstrakte Klassen sind einfach Klassen, bei denen nicht alle Methoden implementiert sind. Da Python keine Interfaces kennt, mache ich das üblicherweise so: class X: def tu_was(self, was, das, nocheines): raise NotImplementedError Ab Python 2.6 gibt es auch das Modul `abc`, mit dem man Abstract Base Classes entsprechend markieren kann. http://docs.python.org/library/abc.html -- Schönen Gruß - Regards Hartmut Goebel Dipl.-Informatiker (univ.), CISSP, CSSLP Goebel Consult Spezialist für IT-Sicherheit in komplexen Umgebungen http://www.goebel-consult.de Monatliche Kolumne: http://www.cissp-gefluester.de/ Goebel Consult mit Mitglied bei http://www.7-it.de
participants (7)
-
Andreas Jung
-
DasIch
-
Hartmut Goebel
-
Johannes Markert
-
Mike Abel
-
Sebastian Bechtel
-
Stefan Schwarzer