Exceptions

Wahrscheinlich hat jeder schon mal exceptions gesehen:


import sys
print ("Hallo ",  sys.argv[1])


Wenn man das ohne Argument startet
python3 Hello2.py
bekommt man
Traceback (most recent call last):
File "Hello2.py", line 2, in ?
print ("Hallo ", sys.argv[1])
IndexError: list index out of range
weil man versucht auf argv[1] zuzugreifen, das aber nicht existiert, wenn man keine weiteren Argumente beim Aufruf übergibt. ist.


Zwei Arten das zu vermeiden:

Die klassische mittels control-statements:


import sys
if len(sys.argv) > 1 :
    print ("Hallo ",  sys.argv[1])
else:
    print ("Hallo ",  "wer auch immer da hockt")
#
    


Dadurch wird verhindert dass Exception auftritt.


Und die moderne via try–except


import sys
try:
    print ("Hallo ",  sys.argv[1])
except:
    print ("wer auch immer da hockt")
    # Programm bricht nicht ab !

print ("\n Und weiter geht's ...")
#
    


Das fängt die Exception auf innerhalb des except: Blocks.


Wozu das Ganze ?
Programme sollen robust sein, d.h. sie sollen falsche Eingaben, extreme Datenwerte oder generell unerwartete Situationen vernünftig abfangen und nicht bei jeder Kleinigkeit das ganze Programm abbrechen.

Die klassische Methode mit if Abfragen hat eine Reihe von Nachteilen

Exceptions bieten elegante Lösung, insbesondere können Sie nicht einfach ignoriert werden.

if Abfragen vs Exceptions ist klassisch–philosophisches IT–Problem, siehe z.B. LBYL vs EAFP.


Exceptions sind in Python als Klassen definiert und hierarchisch aufgebaut. Hier die wichtigsten, ausgehend von der Basisklasse Exception:

    Exception
     ..
     +-- StandardError
     |    +-- KeyboardInterrupt
     |    +-- ImportError
     |    +-- EnvironmentError
     |    |    +-- IOError
     |    +-- EOFError
     |    +-- RuntimeError
     |    +-- AttributeError
     |    +-- TypeError
     |    +-- AssertionError
     |    +-- LookupError
     |    |    +-- IndexError
     |    |    +-- KeyError
     |    +-- ArithmeticError
     |    |    +-- OverflowError
     |    |    +-- ZeroDivisionError
     |    |    +-- FloatingPointError
     |    +-- ValueError
     |    |    +-- UnicodeError
     |    |        +-- UnicodeEncodeError
     |    |        +-- UnicodeDecodeError
     |    |        +-- UnicodeTranslateError
     |    +-- ReferenceError
     |    +-- SystemError
     |    +-- MemoryError


Verschiedene Optionen mit Exceptions umzugehen

Einfach ignorieren, dann Abbruch des Programms wenn Exception auftritt.

Oder man fängt sie mit try–except ab:

Generisch jede Exception


#M=[[0.]*2]*1
try:
    determinant = M[0][0]*M[1][1] - M[0][1]*M[1][0]
except Exception:
    print  ("Troubles: ")

#
# Can get more details on the type of exception and problem
try:
    determinant = M[0][0]*M[1][1] - M[0][1]*M[1][0]
except Exception as x:
    print  ("Troubles: ", x.__class__.__name__ , ':', x)
    



Oder spezifisch die einzelnen Möglichkeiten mit mehreren except statements


M=[[0.]*2]*1
try:
    determinant = M[0][0]*M[1][1] - M[0][1]*M[1][0]
except IndexError as x:
    print  ("M is the wrong size to have a determinant.",  x)
except NameError as x:
    print  ("Programming error!  M doesn't exist:  ",  x)

#
    



Ablauf:


Selber Exceptions auslösen

Man kann selbst Exceptions auszulösen, mit
raise SomeExceptionClass


import math
def root( A, B, C):
    """ Returns the two roots of
      the quadratic equation A*x*x + B*x + C = 0.
      (Throws an exception if A == 0 or B*B-4*A*C < 0.)"""
    if (A == 0):
        raise ValueError("A can't be zero.")
    else :
        disc = B*B - 4*A*C
        if (disc < 0):
            raise ArithmeticError("Discriminant < zero.")
        else:
            return (  (-B - math.sqrt(disc)) / (2*A),  (-B + math.sqrt(disc)) / (2*A) )
#

print (root( 1., 5., -3.))  # ok
root( 0., 2., 1.)   # causes ValueError exception
root( 3., 2., 15.)  # causes ArithmeticError exception

Insgesamt ermöglichen Exceptions eine Funktionalität, die allein mit
return errnum und if Abfragen nicht zu erreichen wäre: