Multiple Exceptions -> zurückspringen geht nicht?

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
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

folgendes Problem.
Ich habe eine Funktion die sieht in etwa so aus:

Code: Alles auswählen

def test(a,b):
    try:
        1/a
    except:
        raise a_is_zero
    finally:
        print "Muh"

    try:
        1/b
    except:
        raise b_is_zero
    finally:
        print "bla"    

try:
    test(0,0)
except a_is_zero:
    print "a is Zero"
except b_is_zero:
    print "b is Zero"
Das Problem ist nur, das ich nur ein "a is zero" bekomme statt "a is zero", "b is zero".
Vermutlich liegt das daran, das ich was mit den Exceptions verbassele.
Ich springe anscheinend nicht wieder in test zurück um den Teil mit b überhaupt auszuführen.
Kann mir da jemand einen Tipp geben?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Führst du auch den richtigen Script aus? Ich bekomme ein "a is zero".
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
anogayales
User
Beiträge: 456
Registriert: Mittwoch 15. April 2009, 14:11

Xynon1 hat geschrieben:Führst du auch den richtigen Script aus? Ich bekomme ein "a is zero".
Das bekommt der Threadstartet auch raus.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Naja, was anderes kann auch nicht kommen, eine Funktion kann nur einen Rückgabewert/Exception haben/werfen. Bei dem entsprechenden Statement wird die Funktion verlassen, also kann auch nur eine Exception abgefagen werden, da nur eine existiert. Generell wird es nie zwei Exceptions geben.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@p90: Die Antwort auf die Frage im Titel (gut versteckt!) ist "Ja".
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Das ist natürlich blöd.
Gibt es den keine Möglichkeit dies irgendwie zu umgehen?

Versuche halt immo den logischen Teil meines Programmes vom "ausgebenden" Teil zu trennen und nutze dazu
wenn ein Fehler auftritt eine Exception um so dem aufrufenden Teil den Fehler mitzuteilen und ihn dann im logischen Teil zu korrigieren.
BlackJack

@p90: Dann musst Du das anders organisieren. Aber du korrigierst da ja auch gar nichts!? So wie Du das beschreibst müsstest Du die Ausnahme ja im logischen Teil auch behandeln und nicht erst bis zum Aufrufer nach oben wandern lassen.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

ne ich korrigiere es erst im logischen Teil, reiche dann über eine exception die Nachricht nach oben weiter (da wird dann ein print etc ausgeführt) und dachte eigentlich das dann erst mein finally ausgeführt wird (was auch der Fall ist) und das es dann nach der exception einfach weiter geht.
Aber da habe ich die Funktionsweise von exceptions wohl falsch verstanden.
BlackJack

@p90: Der ``finally``-Block wird ausgeführt *bevor* die Ausnahme weiter oben in der Aufrufhierarchie behandelt wird.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Ah, okay.
Hm, muss ich mir was anderes ausdenken.
Danke für die viele Hilfe und noch eine schöne Nacht!
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Man könnte das ja mit einer dritten Exception lösen, aber wo braucht man das? Will man sowas wirklich, reicht es nicht einfach auf ZeroDivisionError oder einfach auf "== 0" zu prüfen? An welcher Stelle brauchst du denn sowas, vieleicht gibt es da gute alternativen.

Hier mal mit eine dritten Exception - würde ich so nie in irgendeinem Programm haben wollen.

Code: Alles auswählen

def test(a, b):
    try:
        1/a
    except ZeroDivisionError:
        try:
            1/b
            raise AIsZero
        except ZeroDivisionError:
            raise ABAreZero
    try:
        1/b
    except ZeroDivisionError:
        raise BIsZero
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Mir wird ganz schwindlig von dem CAmeLCase. ;)
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Sry, die Namen a und b für Variablen sind aber auch doof :roll:
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@Xynon1: IMHO ganz schlechte Lösung weil es das DRY-Prinzip verletzt. In echtem Code steht da ja nicht nur `1⁄b` sondern irgend etwas komplexeres was man dann per kopieren und einfügen mehrfach im Quelltext zu stehen hat.

@p90: Du müsstest etwas genauer klären was Du genau erreichen willst. Wenn es nur um das Informieren geht, könntest Du eine Rückruffunktion mit hineinreichen. Oder Du könntest das `logging`-Modul verwenden.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

mein Problem ist , das ich Prozentzahlen und Absolute Werte verarbeiten muss.
Das Problem hatte ich schon mal hier beschrieben:
http://www.python-forum.de/viewtopic.php?f=1&t=25983

Um das zu lösen habe ich wie vorgeschlagen eine eigene Klasse "Limits" geschrieben.
Die sieht so aus:

Code: Alles auswählen

#using a limits class so 
#we can create limits which are absolute
#or percentage
class limits:
    def __init__(self):
        #set defaults here AND ONLY HERE
        #so you can easily change this later 
        #if needed
        self.defaultmin = (0.05, True)
        self.defaultmax = (0.95, True)
        self.min = self.defaultmin
        self.max = self.defaultmax

    def setlimits(self, min, max):
        for limit in (min, max):
            if limit is not None:
                if limit[-1:] == "%":
                    tmp = limit[:-1]
                    tmppercentage = True
                else:
                    tmp = limit
                    tmppercentage = False
                try:
                    tmp = float(tmp)
                except ValueError:
                    raise self.limit_no_float
                else:
                    if tmppercentage:
                        tmp = tmp / 100.0
                    setattr(self, limit, (tmp, tmppercentage))
        
        if self.max[1] == self.min[1] and self.min[0] >= self.max[0]:
            self.min = self.defaultmin
            self.max = self.defaultmax
            raise self.lower_bigger_upper
Da fehlen zwar ein paar Methoden, aber die beschäftigen sich nur mit der Ausgabe der Daten für root, hier also nicht interessant.

Nun möchte ich dem Benutzer zur Laufzeit direkt anzeigen welche Werte er eingegeben hat (ich lese sie aus einer von ihm editierten XML Datei aus)
damit er, sollte er einen Fehler finden sofort das Programm stoppen kann und nachsehen kann was los ist. (sobald ich da alles eingelesen habe werden die Daten für eine Analyse verwendet und
die dauert je nach Datenmenge zwischen 1 und 10h. Da möchte man nicht wegen einem Tippfehler von vorne Anfangen nur weil das Prog die Werte nicht nochmal angezeigt hat)
Im Moment habe ich dazu einfache print Anweisungen die dem Benutzer seine Werte mitteilen und falls keine oder invalide Werte eingegeben wurden, wann auf den Default Wert zurückgegriffen worden ist.
Wollte das ganze an sich auch ohne das setlimits() machen und direkt alles bei der Initalisierung verarbeiten, aber dann haut mir eine Exception die Zuweisung von:

Code: Alles auswählen

a = limits(min, max)

weg sodass a dann nicht definiert wäre.

Das logging Module wäre eine alternative aber afaik (zumindest hatte ich da das letzte mal Probleme mit) gibt dieses erst nach der kompletten Ausführung der Funktion die geloggten Werte weiter statt in Echtzeit. Was ich hier ja gerade haben möchte.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ich kann den Zusammenhang zur ursprünglichen Frage nicht herstellen. Wo sind denn da mehrere Exceptions, die Du allesamt auf einmal prüfen und behandeln willst?

Mal davon abgesehen sollten Klassen in Python 2 von `object` erben - oder benutzt Du Python3?

Auf jeden Fall gilt dann, dass man `CamelCase` für Klassennamen benutzt und Funktionen klein_und_mit_unterstrichen benennt.

Wieso hast Du deets Vorschlag nicht umgesetzt? Du scheinst hier eine Klasse für zwei verschiedene Typen bauen zu wollen - wieso das?

Für das Setzen von Werten sind explizite Setter-Methoden im Pythonumfeld eigentlich verpöhnt. Dazu sollte man eher Properties benutzen.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

@p90: Ich weiss nicht ob ich das Problem verstehe!? Du willst, dass wenn der Benutzer falsche Eingaben macht, das Programm nicht abbricht, sondern mit Default-Werten weiter läuft? Das würde ich ja schon mal für einen Entwurfsfehler halten. Wenn da eine falsche Eingabe drin ist, sollte das Programm IMHO abbrechen und das dem Benutzer deutlich sagen, dass er etwas verbessern muss. Gerade bei sonst langen Laufzeiten bei denen am Ende ”Müll” heraus kommen kann.

Die Default-Werte in der `__init__()` würde ich auf die Klassenebene verschieben. Ausserdem könnte man auch eine Wertübergabe an die `__init__()` vorsehen:

Code: Alles auswählen

class Limits(object):
    MIN = (0.05, True)
    MAX = (0.95, True)
    
    def __init__(self, min_=None, max_=None):
        self.min = self.MIN if min_ is None else min_
        self.max = self.MAX if max_ is None else max_
Du versuchst IMHO zu viel auf einmal. Wenn Du einzeln und unabhängig über Fehler informiert werden möchtest, dann musst Du halt zwei verschiedene Methoden zum Setzen schreiben. Plus eine zum Validieren (das letzte ``if``), weil mit zwei unabhängigen Methoden ja zwischenzeitlich ganz legal ein ungültiger Zustand entstehen kann.

In die `__init__()` würde ich es gar nicht verlegen wollen, weil das Parsen von Benutzerangaben da IMHO nicht hinein gehört. Dafür würde ich eher eine Klassenmethode `from_strings()` oder so schreiben. Die `set_*`-Methoden würde ich entsprechend auch `set_limits_from_strings()` bzw. `set_min_from_string()` usw. nennen.
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

@Hyperion
Ich verwende doch genau deets Lösung.
Nur das ich nicht extra zwei Klassen und bei mir die Klasse selber erkennt um was für einen Fall es sich handelt.
Dadurch kann ich auch gemixte Fälle darstellen, was für mich sehr wichtig war und habe die Logik in der Klasse und nicht
bei dem aufrufenden Teil.
Und die exceptions die ich da behandle sind z.B. limit_no_float und lower_bigger_upper
Eigentlich wollte ich limit_no_flaot noch auf upper_limit_no_float und lower_limit_no_float ausdehnen aber da es sich da
um den selben Fehler handelt werde ich diesen in Zukunft über ein Argument von limit_no_float abhandeln.
Was ja nicht geht wie ich jetzt gelernt habe ^^

@BlackJack
Ich mache das mit den Defaultwerten aus folgendem Grund.
Ich habe da zum Teil 20 Auswertungen hintereinander.
Deshalb lese ich erst die ganzen Informationen und die angegebenen Files die für die Analyse gebraucht werden aus.
Dadurch kann der Benutzer sehen ob die Pfade die er angegeben hat auch alle existieren und bekommt nebenbei auch
die Anzahl der Dateien die ausgelesen werden angezeigt.
Hat er nun mehrere Fehler gemacht und was Programm bricht bereits beim ersten ab, kommt es vor, das er die Verzeichnisse für die ersten 10 Auswertungen bereits durchgearbeitet hat.
Dies müsste dann wiederholt werden (und wir sprechen hier von Dateieanzahlen bei denen ein normales ls ca. 2min braucht um sie anzuzeigen und das pro Auswertung).
Deshalb lasse ich einen Fehler ausgeben, wende wenn möglich Default Werte an und lasse es weiterlaufen.
Der Benutzer, der eh warten muss sieht diesen und kann dann entscheiden ob der Fehler so schwer ist, das es abgebrochen werden muss oder ob es okay ist
mit den angezeigten Default werden weiterzuarbeiten.
Bricht er ab, hat er etwas Zeit verloren und das Netzwerk unnötig belastet, ist im der Fehler egal, geht so nichts verloren.
Breche ich aber einfach ab hat er so oder so verloren.
(zur Erklärung, meine Daten liegen halt auf verschiedenen Networkstorages die in das locale Dateisystem eingebunden sind. Diese sind nur mit 100MBit angebunden und werden auch nicht nur von mir benutzt ^^ Die Datenmenge ist im 10GB Bereich, über verschiedene Ordner verteilt und immer in 50kb großen Dateien drin (die immer ein Event enthält). Deshalb minimiere ich die Nutzung von os.walk so weit es geht und speichere mir direkt beim suchen aller Directories und Subdirectories alle gefundenen Filenamen weg. Das nimmt nicht viel Ram weg (irgendwas unter 10MB) aber ich muss den Kram nur einmal übers Netzwerk gondeln)
Aber du hast recht. Ich sollte vor der eigentlichen Auswertung noch eine Abfrage mit "Wollen sie mit der Auswertung der Daten beginnen?" einbauen.
Und die Namen sollte ich auch noch anpassen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

p90 hat geschrieben:@Hyperion
Ich verwende doch genau deets Lösung.
Nur das ich nicht extra zwei Klassen und bei mir die Klasse selber erkennt um was für einen Fall es sich handelt.
Dadurch kann ich auch gemixte Fälle darstellen, was für mich sehr wichtig war und habe die Logik in der Klasse und nicht
bei dem aufrufenden Teil.
Also verwendest Du die Lösung ja grad nicht :-P
Was sind denn gemixte Fälle? Ohne Bezug - und den gibts ja wohl nicht, wie man aus dem alten Thread entnehmen durfte - macht doch eine Prozentangabe zusammen mit einer absoluten keinen Sinn?!?

Eine Parsing-Logik kann man ja auch in eine separate "Fabrik"-Funktion auslagern, die den richtigen Typen erstellt. Ansonsten eben BlackJacks Vorschläge bezüglich eines Parsings in separaten Methoden.

Zur Einleseproblematik: Kannst Du Dir nicht in guten Datenstrukturen merken, was Du bereits eingelesen hast? (Also alle Infos, die dann später für eine Auswertung benötigt werden). Dann kannst Du bei einem Fehler abbrechen und nach der (externen) Korrektur an der Stelle wieder einsteigen, wo Du abbrechen musstest. Damit wäre das in der Tat nervige erneute Durchkauen von bereits einmal analysierten Daten behoben.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
p90
User
Beiträge: 198
Registriert: Donnerstag 22. Juli 2010, 17:30

Hi,

@Hyperion
1. Ich betrachte Energien die ich bei meinen Events gesehen habe.
Und dann verwende ich gemixte Fälle z.B. zu sagen: Fange bei 0eV an und schneide alles über 90% der maximal gemessenen Energie ab (dies trägt einer Eigenheit unserer Messgeräte Rechnung die Signale je nach Einstellung zu klippen also alles größer als 50eV wird dann als verteilt von 45eV bis 50eV angezeigt und somit für mich unbrauchbar.)
Genau diese gemixten Fälle produzieren auch einige Probleme (was ist wenn 0,9* maximum gemessenes kleiner als angegebenes minimum ist? In diesem Fall würde ich z.B. auf die Default Werte zurückgreifen da ich ansosnten umsosnt 10GB Messdaten über das Netzwerk geschaufelt habe

2. Darüber habe ich bereits nachgedacht aber bisher noch keine vernünftige Lösung gefunden. Ich könnte eine (ich glaube JSOD nennt sie sich) Datenbank verwenden um die Ergebnisse zwischen zu speichern. Oder gar ein einfaches XML file. Aber dann bin ich doch wieder genau bei dem selben Problem oder?
Meine "schreib" Funktion merkt z.B. erst nach dem alle Daten gelesen sind ob ein gemischter Fall einen Fehler verursachen wird oder nicht. Ich muss dann also eine Exception werfen um da raus zu kommen und dann den Benutzer anhauen (und nicht nur mit nem einfachen print sondern irgendwie anders....) , dann die Werte anpassen und es nochmal machen.
Hm. Wie mehr ich drüber nachdenke umso bessere Lösungen fallen mir dafür ein...
Also ich werde es auf alle Fälle probieren, an sich sollte das die beste möglichkeit sein (wenn es sich um etwas handelt was ich ausgleichen kann)
Antworten