Error Handling Umfrage: raise vs. return

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.
Antworten

Welche Methode benutzt ihr häufiger?

return ERROR_CODE
4
22%
raise Exception
14
78%
 
Insgesamt abgegebene Stimmen: 18
buergi
User
Beiträge: 10
Registriert: Donnerstag 13. Juli 2006, 18:05

Was haltet ihr für besser, oder was benützt ihr häufiger?

Code: Alles auswählen

def function(intval):
    if intval == 0: return -1
    #...
oder

Code: Alles auswählen

def function(intval):
    if intval == 0: raise ValueError
    #...
Sinnlose Beispiele aber sie verdeutlichen was ich meine :)

Bitte auch Gründe angeben, interessiert mich, weil ich mich nie entscheiden kann wie ich mein error handling umsetzte.

Scheint ja nicht einfach ein "besser" zu geben, schließlich gibt es auch in Python Modulen beide Versionen, ich nenn nur mal die String-Methoden find() <-> index()

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

Ich nutze Exceptions, nach dem EAFP-Prinzip. Das scheint mir irgendwie klarer, da man so Fehler nicht übersieht. Natürlich, es gibt auch Ausnahmen, dass ich manchmal None zurückgebe (aber nie Error-Codes, denn das bringts nicht - total zufällige Nummern sagen mir nichts), aber dann hat das auch einen trifftigen Grund.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
buergi
User
Beiträge: 10
Registriert: Donnerstag 13. Juli 2006, 18:05

Leonidas: Hab ich das richtig Verstanden: Du ignorierst im Prinzip also die möglichen Fehlerquellen? Und delegierst damit die Verantwortung für das Handling an den Anwender der Funktion? Bei kleineren Funktionen kein Problem aber ich denk mir immer, dass das ziehmlich umständlich zu verwenden ist, wenn ich bei jedem Aufruf viele Exceptions abfangen muss.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

buergi hat geschrieben:Leonidas: Hab ich das richtig Verstanden: Du ignorierst im Prinzip also die möglichen Fehlerquellen?
Nein. Die Fehlerquellen zu ignorieren, würde heißen, dass die Funktion im Fehlerfall keine Meldung vom Misserfolg macht, weder Exception noch Error Code. Eine Exception dagegen ist eine sehr gute Methode, um zu zeigen, dass in der Funktion ein Fehler aufgetreten ist.
buergi hat geschrieben:Und delegierst damit die Verantwortung für das Handling an den Anwender der Funktion?
Die Verantwortung hat immer der Anwender der Funktion: ob er nun eine Exception von der Funktion bekommt oder einen Fehlercode als Rückgabewert. Aber eine Exception ist schwieriger zu übersehen.
buergi hat geschrieben:Bei kleineren Funktionen kein Problem aber ich denk mir immer, dass das ziehmlich umständlich zu verwenden ist, wenn ich bei jedem Aufruf viele Exceptions abfangen muss.
Andererseits ist es fehleranfällig, jede Funktion auf den Rückgabewert zu prüfen.

Gehen wir mal davon aus, das wir eine Funktion haben, die zwei Argumente hat, du ihr aber aus versehen nur eines angibst. Python wirft eine Exception. Nehmen wir an, dass Python statt einer Exception den Rückgabewert 3735928559 zurück gibt. Dann müsstest du jeden Funktionsaufruf prüfen, ob der Rückgabewert nicht zufällig 3735928559 ist, was heißt, dass du die falschen Parameter angegeben hast. Das tolle einer Exception ist auch, dass sie neben dem Typ auch noch Botschaften enthalten kann, wo seht was genau der Fehler ist. Du kannst sogar eigene Objekte als Exceptions werfen, die noch andere tolle Sachen machen, wie zum Beispiel automatisch etwas ins Syslog eintragen oder im Falle von Webseiten automatisch eine Mail an den Webmaster schicken. Das bietet ein Rückgabewert nicht.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Wie Leonidas schon sagte, Exceptions sind nicht zu übersehen, und der Vorteil ist auch, dass wenn ein Codeteil sich nicht drum kümmert, der aufrufende Teil das mitbekommt. Beispiel:

Code: Alles auswählen

def a(x):
 if x<0:
  raise ValueError
 return x*2
def b():
 x = a()
 return x*2
def c():
 try:
  b()
 except:
  # irgendwass stimmt hier nicht
Nehmen wir jetzt mal an, a() würde im Fehlerfalle -1 zurückgeben. b() wurde aber schnell zusammengehackt und interessiert sich nicht dafür. c() hätte jetzt keine Ahnung, dass b mit dem Fehlercode -1 einfach weiterrechnet und totalen blödsinn produziert.

In C hat man keine Exceptions, und mal ehrlich: Wer überprüft in schnellen Hacks schon, ob malloc nicht NULL zurückgegeben hat oder open -1? Und was ist nun, wenn der Hack auf einem Produktivsystem eingesetzt wird, weil er gut zu funktionieren scheint?

Insbesondere die frühen Programme hatten ein oder zwei Stellen, wo soetwas nicht abgefragt wurde. Der Code, der darauf zugegriffen hat, hatte in der Regel keine Chance, das festzustellen.

Wenn dich im wahren Leben jemand fragt, wie alt "Susanne Kwertobutzno" ist, sagst du ja auch nicht "99 Jahre", sondern sagst wahrscheinlich "Susanne Wer? Nie gehört!"
Benutzeravatar
Masaru
User
Beiträge: 425
Registriert: Mittwoch 4. August 2004, 22:17

*grübel* ... *grübel* ... habe nun meinen Post 3-mal begonnen, und irgendwie immer noch nicht so den richtigen Ansatz gefunden.

Was die ERROR_CODES Variante angeht, so nutzte ich Error Codes eher als finale Fehlerindikation wenn ich mein Programm in einem Fehlerfall abbrechen lasse. In solchen Fällen ist es für ein z.B. aufrufendes Programm von Vorteil, wenn dieses an Hand von Codes die Fehlerfälle unterscheiden kann (und nicht die Fehlermeldung erst geparst werden muss ... was im Falle von "Language-Support" schon echt eine grausame Aufgabe sein kann).

Sowas erfordert natürlich eindeutige Codes, eine ausführliche Dokumentation dieser sowie vielleicht eine Anlehnung an typische, bekannte Codes (z.B. in Bezug auf das verwendete Betriebssystem, o.ä.).

Bsp.:

Code: Alles auswählen

def abort(text, error_code):
    error_msg = "ERROR: %s" % text
    sys.stderr.write(error_msg)
    sys.exit(error_code)

# abort("File '%s' not found" % 'test.txt', 2)
# abort("Internal Error", -1)
# abort("Access denied", 401)
# ...
Intern finde ich die Verwendung von Error Codes persönlich eher witzlos ... sie sind einfach schwer zu lesen, schwer nachzuverfolgen und noch schwerer zu pflegen.

Funktionatlitäten sollten so gestrickt sein, dass wenn sie mit Rückgabewerten arbeiten auch entsprechend eindeutige Werte zurückgeben können.

Es macht meiner Erfahrung nach mehr Sinn z.B. eine Methode

Code: Alles auswählen

def is_system_active(self):
    if self.status in ['inaktiv', 'off', 'schlafend']:
        return False
    if self.energy <= 0:
        return False
    return True
z.B. zu verfassen, und mit dem boolschen Erfolgsindikation weiter zu arbeiten, als mit Fehlercodes mit mehr als Wahr oder Falsch verarbeiten zu wollen.

Für mehr Komplexität in Bezug auf die Fallunterscheidung gibt es ja schließlich immerhin ja noch die Methodik des Zustandsautomaten.

Was hingegen Exception-Handling angeht, so ist dies auch immer wie mit einer Schwingtür: Läufs't zum richtigen Zeitpunkt durch ... wunderbar, andernfalls schlägst dir im ungünstigen Falle ne blutige Nase.

Betreibt man Exception-Handling "übersichtlich" und sowohl im Wissen darüber welche Exceptions wo genau geworfen werden können, sowie auch der Tatsache der Anwendung des expliziten Exception-Catchings (und nicht der bequemen und einfachen Anwendung des "Catch-All"-Prinzips ;) ) ... dann ist es manchmal echt ne tolle Sache.

In einem Projekt ist mir einmal untergekommen, dass ein spezielle Fehlerklasse (nennen wir sie mal "AppError") mit einem Fehlertext inform eines Codes, sowie Fehlertext-Paramtern geworfen wurde. Der Fehlercode holte sich den Fehlertext aus der Datenbank und füllte dort Platzhalter mit den erwähnten Fehlertext-Paramtern aus ... abgefangen wurde "nur" diese spezielle Fehlerklasse von dem verantwortlichen XML-RPC-API oder WebApp-API Modul.
So konnte aus dem Prozess eines Aufrufes seitens des Benutzer herraus gleich einer Fehlernachricht angezeigt werden, ohne komplizierte Rückgabepfade durchlaufen zu müssen.

Es gibt in meinen Augen also halt Architekturen und Stellen wo gerade Exceptions DIE Lösung schlecht hin sind.


Was natürlich totaler Bullshit hingegen ist, dass ist der Versuch Exception als finale Fehlerindikation im Rahmen des Programmabbruches zu verwenden, welche externe (vielleicht gar in anderen Programmiersprachen geschriebene) Programme oder ein End-Benutzer informieren sollen.

Code: Alles auswählen

c:\daily_backup.exe
[2006-07-30 01:24] Backup is running ...
[2006-07-30 01:25] Prepare path-copy: "d:\Eigene Dateien" ...
Traceback (most recent call last):
  File "<stdin>", line 25, in copy_path
NameError: name 'ValuError' is not defined
"... hm ... äh ... jap ... omg ... Huston?"


Zurück zur Umfrage fehlt mir allgemeinen deswegen ein Punkt für beides gleichermaßen, wenn als Error_Codes auch ein einfaches True or False gillt ;).


Gruß,
>>Masa<<
Mad-Marty
User
Beiträge: 317
Registriert: Mittwoch 18. Januar 2006, 19:46

Ganz klar Exceptions intern !

Einfacher, Effizienter, Sicherer.

Fehlercodes sind eher was für minor sachen oder returns an den anwender des programms. Nie für bibliotheken ... ausser es sind blos warnings.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Ich bin absoluter Freund von Exceptions. Ich verwende sie überall bis auf sys.exit() ;-)
TUFKAB – the user formerly known as blackbird
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Masaru hat geschrieben:Was natürlich totaler Bullshit hingegen ist, dass ist der Versuch Exception als finale Fehlerindikation im Rahmen des Programmabbruches zu verwenden, welche externe (vielleicht gar in anderen Programmiersprachen geschriebene) Programme oder ein End-Benutzer informieren sollen.
Deswegen schreibt man ja auch in einem Programm, das für Enduser gedacht ist, einen schönen Catch-All-Handler und loggt die eigentliche Exception irgendwo (oder stellt sie dem Benutzer nur auf Nachfrage zur Verfügung).
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Jup, bin auch für Exceptions... Ist denke ich der sauberste Weg...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
buergi
User
Beiträge: 10
Registriert: Donnerstag 13. Juli 2006, 18:05

ok thx2all war sehr informativ, ich werde euren Rat befolgen :) ich fand Exceptions auch besser, bis auf die Tatsache, dass man nie weiß welche Funktion jetzt welche Exceptions raised (v.a. bei Biblioteken), aber kriegt man auch leicht raus, man muss halt docustrings orso benutzen.

Noch eine Frage: Versucht ihr build-in Exceptions zu benutzen, oder schreibt ihr lieber neue exception klassen, die dann speziell für eure Programme gemacht sind?
Also ValueError, TypeError orso is klar. Aber wenn man z.B. eine Library benutzt, die ja meistens eigene Exceptions mitbringen, ist es wohl besser nicht diese Exceptions durch zu geben oder?
Also Beispiel: ich benutze z.B. ftplib , die gibt bei falscher URL gaierror(aus module-socket)zurück, sollte ich dann auch diese Exception verwenden oder eine eigene Schreiben?
buergi
User
Beiträge: 10
Registriert: Donnerstag 13. Juli 2006, 18:05

Mir ist noch ein Fall den ich erst vor kurzem hatte wo ich auch nicht wusst wie ich das mit Exceptions löse:

Das Einlesen einer Datei, z.B. comma seperated list
Es könnten jetzt ja einzelne Zeilen falsches Format haben, aber trotzdem kann ich die anderen, mit korrekter Syntax ja benutzen. Wenn ich jetzt jedoch ne Exception raise bricht die Funktion ja ab und liest nicht die restlichen, evtl. korrekten Zeilen ein.

Ich habs bis jetzt einfach so gemacht, dass die Funktion in einer Liste nur die korrekten Zeilen zurückgibt, und zusätzlich noch eine Variable, die die Anzahl der Fehler enthält. Wisst ihr was besseres? Sollte ich z.B. die Anzahl der Fehler mit Hilfe einer Exception zurückgeben, die geraised wird wenn die Funktion alle Zeilen eingelesen hat?

Viele Fragen, hoffentlich viele Antworten, Danke schonmal.
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Hallo,

hier mein Senf - ohne, daß ich mich an der Umfrage beteiligen wollte.
Ich nutze bei "richtigen" Pythonprogramm ausschließlich Exceptions. Eine tolle Sache. Wenn das Programm eine kritische Größe überschreitet, passiert auch mal was Unvorhergesehenes und das führt dann beim ersten Mal zum Abbruch, kann danach aber abgefangen werden. Auf diese Weise finde ich mich selber sehr viel besser nach ein paar Monaten im Code zurecht und andere Leute können es auch besser nutzen (wenn zum Beispiel eine schöne Fehlermeldung auftaucht).
Andereseits benutze ich folgendes Konstrukt manchmal in einfachen Skripten:

Code: Alles auswählen

if x:
    __info__ = """ etwas Info """
    sys.exit(__info__)
Das ist zwar nicht so pythonic, hat aber den Vorteil, daß es von Shellskripten gut abgefangen werden kann, weil der Returnvalue ungleich 0 ist.

@buergi:
Den Fall habe ich recht häufig, weil ich verschiedene Programme nutze, die Daten in den unterschiedlichsten Formaten rausgeben. Ein Bespiel für meinen Weg:

Code: Alles auswählen

def _read_gnom_scatter_data(fname):
    """
        reads in scattering data from a gnom output file
        returns q's and I(q)
        
        invoke like:
        x = _read_gnom_scatter_data(fname)
        where x will be a tuple of q,q2,J_exp,error,J_reg,I_reg -
        all data are numpy arrays
    """
    q = [] #momentum transfer, all values
    q2 = [] #momentum transfer, part of the values
    J_exp = [] #experimental data
    error = [] #experimental error
    J_reg = [] #fit through experimental data
    I_reg = [] #desmeared data
    sys.stdout.write('Working on ... %s            \r' % fname.split('/')[-1])
    sys.stdout.flush()
    f = open(fname,'rU')
    #walk through all lines in the input file:
    flag = False
    for line in f.readlines():
        if flag:
            try:
                x = [float(x) for x in line.split()]
                if len(x)>0: #line not empty?
                    #since q extends from 0 to infinity and I_reg as well
                    #ask, wether x is greater than 2:
                    if len(x)>2:
                        q.append(x[0])
                        q2.append(x[0])
                        J_exp.append(x[1])
                        error.append(x[2])
                        J_reg.append(x[3])
                        I_reg.append(x[4])
                    else:
                        q.append(x[0])
                        I_reg.append(x[1])
            #EOF (almost) reached? -> set flag = False and read in remaining data:
            except ValueError:
                flag = False
        #encountered table with p(r)-function?
        if "S          J EXP       ERROR       J REG       I REG" in line: 
            flag = True
            #skip the following two lines in a gnome ouput file
            f.readline()#; f.readline()
    f.close()
    
    #convert all values into arrays:
    q = array(q) 
    q2 = array(q2) 
    J_exp = array(J_exp)
    error = array(error)
    J_reg = array(J_reg)
    I_reg = array(I_reg)
    
    return q,q2,J_exp,error,J_reg,I_reg
Gruß,
Christian
buergi
User
Beiträge: 10
Registriert: Donnerstag 13. Juli 2006, 18:05

@Christian
sry ich versteh des snippet irgendwie net :(
funktioniert des wirklich? wenn du vorher schon ein for line in f.readlines() machst liest python doch schon des ganze file, dann hat doch das f.readline() in zeile 47 keinen einfluss mehr ?
Dein Script überspringt einfach das was es nicht brauch kann oda?
So machs ich auch einfach alles was Müll is ignorieren in der Datei :)
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

Stimmt, inzwischen kommt es nur noch auf den flag an - hätte ich schon längst ändern sollen, habe ich aber übersehen. Ist zwar ein bug, aber nicht kritisch und so ist es mir einfach nicht aufgefallen. Danke.
Wesentlich ist, daß ich zwar weglasse, was ich nicht brauchen kann - aber eben selektiv. Ich wandele ja in floats um, wenn das nicht geht, gibt es einen ValueError. Und auch den nur in einem best. Bereich des Files. In allen anderen Fällen würde der Fehler eben nicht ignoriert. (Und das passiert, wenn aus irgeneinem Grund das Inputfile korrumpiert ist.)

Gruß,
Christian
Antworten