Python

  • Aktuelle Version: Python 3.6
  • Interpretierte Programmiersprache
    • Kein Kompilieren
    • Programme werden mit dem python-Programm ausgeführt
  • Eignet sich sehr gut zum Erlernen der Programmierung!
  • Hat viele Pakete, die man für wissenschaftliche Arbeit gebrauchen kann

Hier soll es hingehen: Auswertung von Praktikumsversuchen:

Plot

Versuch aus dem Fortgeschrittenen-Praktikum: Lebensdauer kosmischer Myonen

Python

Eine Programmiersprache wie Python lässt sich nicht an einem Tag lernen.

Hier geben wir einen kurzen Überblick über die wichtigsten Konzepte.

Keine Angst: Zum Erstellen von Plots und für einfache Auswertungen muss man nicht viel Python können!

Die folgenden Beispiele kann man auf der Kommandozeile durch ausführen von

ipython

ausprobieren.

Einfachstes Beispiel

In [1]:
print('Hello, World!')
Hello, World!

Kommentare

Sehr wichtig für die Leute, die mit euch Arbeiten und für euch zwei Wochen später, sind Kommentare, die erklären, was euer Code tut.

Alles was in Python hinter einem # steht, wird nicht als Code betrachtet und dient als Kommentar.

In [2]:
# This just prints the traditional greeting to the console
print("Hello, World!")
Hello, World!

Kommentare sollten nicht erklären, was der Code tut, sondern warum.

Kommentare, wie jeder andere Code, sollten auf englisch sein.

Variablen

Man kann einem Objekt einen Namen geben:

In [3]:
a = 2
b = 3

Variablennamen können auch überschrieben werden:

In [4]:
a = 42
b = a

a = 0
b
Out[4]:
42

Einfache Datentypen

None

Stellt das Fehlen eines Wertes dar.

Wird häufig für Standardwerte von Funktionen verwendet.

In [5]:
print(None)
None

Funktionen, die 'nichts' zurückgeben, geben None zurück:

In [6]:
print('test')
test
In [7]:
a = print('test')
test
In [8]:
print(a)
None

Booleans

True und False

Logische Operatoren: and, or, not

In [9]:
True and False
Out[9]:
False
In [10]:
True or False
Out[10]:
True
In [11]:
not True
Out[11]:
False

None oder leere Objekte verhalte sich wie False, andere wie True

In [12]:
None or True
Out[12]:
True
In [13]:
'Hallo' and True
Out[13]:
True

Zahlen

Ganze Zahlen: 42 Kommazahlen: 3.14

Rechenoperatoren:

  • +, -, *
  • ** (Potenzieren: 2**38)
  • / (3 / 21.5)
  • // (Ganzzahldivision: 3 // 21)
  • % (Modulo-Operation, Divisionsrest: 7 % 43)
In [14]:
x = 5
y = 3
x + y
Out[14]:
8

Rechenoperationene können geklammert werden

In [15]:
a = 2 + 4 / 5 + 1
b = (2 + 4) / 5 + 1
c = (2 + 4) / (5 + 1)
In [16]:
print(a)
3.8
In [17]:
print(b)
2.2
In [18]:
print(c)
1.0

Vergleichsoperatoren geben True oder False zurück

  • ==, !=
  • >, <, >=, <=
In [19]:
x < y
Out[19]:
False

Mehrere Vergleichsoperatoren können in einem Ausdruck stehen:

In [20]:
c < b < a
Out[20]:
True
In [21]:
c < a < b
Out[21]:
False

Strings

Stehen in ' oder ".

In [22]:
foo = 'foo'
bar = "bar"

Strings können mit + konkateniert werden:

In [23]:
foo + " " + bar
Out[23]:
'foo bar'

Strings können mit * vervielfacht werden

In [24]:
foo * 4
Out[24]:
'foofoofoofoo'

Strings können mit [] indiziert werden

In [25]:
foo[0]
Out[25]:
'f'

Listen

Gedacht für mehrere Werte vom selben Typ.

In [26]:
names = ['foo', 'bar']

Listen können mit [] indiziert werdern

In [27]:
names[1]
Out[27]:
'bar'

Man kann Werte an Listen anhängen:

In [28]:
names.append('baz')
names
Out[28]:
['foo', 'bar', 'baz']

Negative Indizes (beginnend mit -1), indizieren die Liste rückwärts

In [29]:
names[-1]
Out[29]:
'baz'

Man kann auch Teillisten indizieren.
names[Anfang:Ende] liefert eine Teilliste mit den Werten von names[Anfang] bis names[Ende-1]:

In [30]:
names[1:3]
Out[30]:
['bar', 'baz']

Listen werden mit + zusammengeführt:

In [31]:
names + ['thing']
Out[31]:
['foo', 'bar', 'baz', 'thing']

Listen können mit * vervielfacht werden:

In [32]:
names * 3
Out[32]:
['foo', 'bar', 'baz', 'foo', 'bar', 'baz', 'foo', 'bar', 'baz']

Man kann die Liste auch erweitern:

In [33]:
names.extend(['quux'])
names
Out[33]:
['foo', 'bar', 'baz', 'quux']

Listen sind veränderbar, man kann ihnen Werte zuweisen:

In [34]:
names[1] = 'new'
names
Out[34]:
['foo', 'new', 'baz', 'quux']

Mit in prüft man, ob ein Wert in der Liste ist:

In [35]:
weekdays = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
'Mo' in weekdays
Out[35]:
True

Tupel

Gedacht für Werte mit unterschiedlichen Typen. Unveränderbar nach Erzeugung. Können wahlweise auch ohne Klammerngeschrieben werden (wenn es eindeutig ist).

In [36]:
tup = 5, 3
tup
Out[36]:
(5, 3)
In [37]:
a = 5
b = 3
a, b = b, a
In [38]:
print("a =", a)
print("b =", b)
a = 3
b = 5
In [39]:
tup[0]
Out[39]:
5
In [40]:
tup[1] = 7
--------------------------------------------------------------------------
TypeError                                Traceback (most recent call last)
<ipython-input-40-ac9eff4a13ef> in <module>()
----> 1 tup[1] = 7

TypeError: 'tuple' object does not support item assignment

Dictionaries

In [41]:
numbers = { 'one': 1, 'two': 2, 'three': 3 }
In [42]:
numbers['two']
Out[42]:
2

Dictionaries sind extrem hilfreich, zum Beispiel um die gleiche Analyse für verschiedene Datensätze durchzuführen:

In [43]:
data = {'Cu': [1.1, 1.2, 1.3, 1.4], 'Fe': [0.7, 0.8, 0.9, 1.0]}
data['Cu']
Out[43]:
[1.1, 1.2, 1.3, 1.4]

Leere Dictionaries erstellt man z.B. mit der Funktion dict()

In [44]:
data = dict()
data
Out[44]:
{}
In [45]:
data["Fe"] = [1.1, 1.2, 1.3, 1.4]
data
Out[45]:
{'Fe': [1.1, 1.2, 1.3, 1.4]}
In [46]:
data["Cu"] = [0.7, 0.8, 0.9, 1.0]
data
Out[46]:
{'Cu': [0.7, 0.8, 0.9, 1.0], 'Fe': [1.1, 1.2, 1.3, 1.4]}
In [47]:
data["Au"] = [0.2, 0.3, 0.1, 0.5]
data
Out[47]:
{'Au': [0.2, 0.3, 0.1, 0.5],
 'Cu': [0.7, 0.8, 0.9, 1.0],
 'Fe': [1.1, 1.2, 1.3, 1.4]}

In der aktuellen Python-Version 3.6 entspricht die Reihenfolge der Einträge in Dictionaries der
Zuweisungsreihenfolge! In allen älteren Versionen war dies nicht der Fall und es kann sich auch
wieder ändern. $\Rightarrow$ Nicht auf die Reihenfolge der Dictionary-Einträge verlassen.

Funktionen aufrufen

Mit Funktionen lässt sich Code leicht wiederverwenden. Eine Funktion nimmt Parameter, verarbeitet sie und gibt Ergebnisse zurück.

In [48]:
print('Hello!')
Hello!
In [49]:
len([1, 2, 3, 4, 5])
Out[49]:
5

Viele Funktionen haben mehrere Parameter, einige können sogar beliebig viele haben:

In [50]:
print(1, 2, 3, 4, 5)
1 2 3 4 5

Viele Funktionen haben optionale Parameter mit eigenem Namen, sogenannte "keyword arguments":

In [51]:
print(1, 2, 3, sep=', ')
1, 2, 3

Objekte haben auch Funktionen (nennt man Methoden):

In [52]:
s = 'test'
s.upper()
Out[52]:
'TEST'
In [53]:
'foo bar baz'.split()
Out[53]:
['foo', 'bar', 'baz']

In IPython kann man wie folgt auf die Dokumentation einer Funktion zugreifen:

In [54]:
print?

Ansonsten findet man ausführliche Erklärungen in der offiziellen Dokumentation.

Kontrollstrukturen

Die Zeilen eines Python-Programms werden nacheinander ausgeführt.

Sogennante Kontrollstrukturen erlauben es, den Ablauf zu steuern.

  • if/elif/else: Je nach Bedingung unterschiedliche Befehle ausführen
In [55]:
a = 3

if a == 1:
    # muss eingerückt werden, 4 spaces:
    print('foo')
elif a == 2:
    print('bar')
else:
    print('baz')
baz
  • while-Schleifen: Wiederholen, solange ein bestimmter Ausdruck erfüllt ist.
In [56]:
i = 0
while i < 5:
    print(i)
    i += 1

# there has to be a better way!
0
1
2
3
4
  • for-Schleifen: Wiederholen für jedes Element (z.B. einer Liste)
In [57]:
data = [10, 42, -1]

for x in data:
    # Wir können auf das jetztige Element als "x" zugreifen
    print(2 * x)
20
84
-2
In [58]:
for i in range(5):
    print(i)
0
1
2
3
4
In [59]:
for i in range(2, 5):
    print(i)
2
3
4
In [60]:
for i in range(10, 3, -1):
    print(i)
10
9
8
7
6
5
4
In [61]:
# Hatten wir weiter oben bei den Listen erstellt
weekdays
Out[61]:
['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']
In [62]:
for day in weekdays:
    print("Heute ist", day)
Heute ist Mo
Heute ist Di
Heute ist Mi
Heute ist Do
Heute ist Fr
Heute ist Sa
Heute ist So

Zwei sehr nützliche Funktionen sind enumerate und zip

In [63]:
for i, day in enumerate(weekdays):
    print(day, " ist der ", i+1, ". Tag der Woche", sep="")
Mo ist der 1. Tag der Woche
Di ist der 2. Tag der Woche
Mi ist der 3. Tag der Woche
Do ist der 4. Tag der Woche
Fr ist der 5. Tag der Woche
Sa ist der 6. Tag der Woche
So ist der 7. Tag der Woche

Was enumerate() macht:

In [64]:
list(enumerate(weekdays))
Out[64]:
[(0, 'Mo'), (1, 'Di'), (2, 'Mi'), (3, 'Do'), (4, 'Fr'), (5, 'Sa'), (6, 'So')]
In [65]:
english = ["foot", "ball", "goal"]
german = ["Fuß", "Ball", "Tor"]

for a, b in zip(english, german):
    print(a, b)
foot Fuß
ball Ball
goal Tor

Was zip() macht:

In [66]:
list(zip(english,german))
Out[66]:
[('foot', 'Fuß'), ('ball', 'Ball'), ('goal', 'Tor')]

Das obere würde man eher so machen:

In [67]:
translations = {
    'foot': 'Fuß',
    'ball': 'Ball',
    'goal': 'Tor',
}

for e, g in translations.items():
    print(e, g)
foot Fuß
ball Ball
goal Tor

Eigene Funktionen definieren mit def

Bevor man eine Funktion verwenden kann, muss sie erst definiert werden. Das wird wie folgt gemacht. Mit dem Keyword return kann man die Funktion beenden und Ergebnisse zurückgeben.

In [68]:
def add(x, y):
    z = x + y
    return z

add(2, 2)
Out[68]:
4

Man kann auch mehrere Werte zurückgeben:

In [69]:
def divide(x, y):
    return x // y, x % y

n, rest = divide(5, 3)
n, rest
Out[69]:
(1, 2)
In [70]:
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

factorial(4)
Out[70]:
24

Module und import

In Python kann man Programme in einzelne Module portionieren.

Es stehen bereits unglaublich viele Module für Python zur Verfügung. Gerade fürs wissenschaftliche Arbeiten ist eigentlich alles da was man braucht und noch viel mehr. Braucht man etwas aus einem anderen Modul, importiert man es:

In [71]:
import os

os.listdir()
Out[71]:
['numeric-python.ipynb',
 'README.md',
 'uncertainties.html',
 'python.ipynb',
 'example_data.txt',
 'test.txt',
 'muon_data.txt',
 'plot_1.pdf',
 'fit_data_with_init_values.txt',
 'Indexing2D_code.svg',
 'muon_plot.py',
 '.gitignore',
 'example2.txt',
 'Indexing1D.svg',
 'Makefile',
 'matplotlib.ipynb',
 'numeric-python.html',
 'Indexing2D.svg',
 'matplotlib.html',
 'scientific-python.html',
 'muon_plot.png',
 'matplotlibrc',
 'plot_2.pdf',
 'uncertainties.ipynb',
 'scientific-python.ipynb',
 'plot.pdf',
 'Indexing1D_code.svg']

Man kann auch nur einzelne Funktionen importieren:

In [72]:
from os.path import join
outputpath = "Plots"
join(outputpath, "fig1.pdf")
Out[72]:
'Plots/fig1.pdf'

from module import * importiert jede Funktion eines Moduls. Sollte auf Grund von möglichen Mehrfachbelegungen nicht in größeren Programmen verwendet werden

In [73]:
from os.path import *
outputpath = "Plots"
if exists(outputpath):
    print("Plots exists!")

Den Modulen können mit as neue Namen gegeben werden

In [74]:
import os.path as pa

pa.join(outputpath, "fig1.pdf")
Out[74]:
'Plots/fig1.pdf'

Auslagern in Python-Dateien

  • Python-Dateien haben die Endung .py
  • Startet man mit python programm.py
  • Einzelne Python-Dateien (Endung .py) kann man als Module benutzen. Liegt eine Datei test.py im selben Ordner, so kann man import test ausführen.
  • Die Datei sollte immer in der Codierung UTF-8 abgespeichert werden.

Es folgt ein fertiges Python-Programm.

Speichert es als primes.py ab und startet es aus dem Terminal mit python primes.py.

In [75]:
def primes(max_num):
    # Only primes smaller than max_num are calculated
    is_prime = max_num * [True]
    is_prime[0] = False
    is_prime[1] = False

    primes = []

    # Sieve of Erathosthenes:
    for i in range(2, max_num):
        if is_prime[i]:
            for j in range(2 * i, max_num, i):
                # Multiples are not primes
                is_prime[j] = False
            primes.append(i)
    
    return primes

print(primes(100))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

Besonderheiten bei Strings

Mit \ kann man in Strings besondere Anweisungen setzen:

  • '\n' -> Zeilenumbruch
  • '\t' -> Tab
  • '\\' -> normales '\'

Wenn man viele '\' schreiben muss (z.B. in LaTeX-Code), lohnt es sich diese Funktion mit dem Prefix r auszuschalten:

In [76]:
print(r'\Huge\texttt{Python}')
\Huge\texttt{Python}

Oft will man Werte oder andere Strings in einen String einsetzen.

Seit der Python Version 3.6 gibt es sogenannte f-strings (formatted strings) für String-Formatierung:

In [77]:
first_value = 42
second_value = 0

print(f"Erster Wert: {first_value}, Zweiter Wert: {second_value}")
Erster Wert: 42, Zweiter Wert: 0

Die Variablen in den {}-Markierungen werden durch die Werte der entsprechenden
Variablen ersetzt.

f-strings haben viele fortgeschrittene Funktionen.

Man kann beispielsweise die gewünschte Genauigkeit für floats angeben:

In [78]:
result = 3.2291421

print(f'Das Ergebnis ist {result:.2f}')
Das Ergebnis ist 3.23

Man kann die Variablen in f-strings auch verschachteln.
Zum Beispiel kann man die Genauigkeit für floats auch als Variable angeben:

In [79]:
result = 3.2291421
precisions = [1,2,3,4,5]

for precision in precisions:
    print(f'Das Ergebnis ist {result:.{precision}f}')
Das Ergebnis ist 3.2
Das Ergebnis ist 3.23
Das Ergebnis ist 3.229
Das Ergebnis ist 3.2291
Das Ergebnis ist 3.22914

Die f-strings sind eine lesbarere Variante einer allgemeineren Funktionalität, der format()-Methode:

In [80]:
first_value = 42
second_value = 0

'Erster Wert: {}, Zweiter Wert: {}'.format(first_value, second_value)
Out[80]:
'Erster Wert: 42, Zweiter Wert: 0'

Die {}-Markierungen werden durch die Parameter von format() ersetzt.

format() bietet noch einige weitere Möglichkeiten, wenn f-strings mal nicht reichen.

Man kann Parameter benennen und diese genauso formattieren wie in f-stings:

In [81]:
'Das Ergebnis ist {result:.2f} und damit kleiner als {four}'.format(result=3.2291421, four=4)
Out[81]:
'Das Ergebnis ist 3.23 und damit kleiner als 4'

Will man { oder } im Text stehen haben (gut für LaTeX) geht das mit doppelten {{ oder }}.

LaTeX sieht dann leider so aus:

In [82]:
print(r'\SI{{{:.4f}}}{{{:s}}}'.format(1.23456, r'\kilo\joule'))
\SI{1.2346}{\kilo\joule}

Nur zur Demonstration einmal mit mehr Lücken (funktioniert so nicht in LaTeX wegen den Leerzeichen!):

In [83]:
print(r'\SI {{ {:.4f} }} {{ {:s} }}'.format(1.23456, r'\kilo\joule'))
\SI { 1.2346 } { \kilo\joule }

Ausführliche Beispiele zur Benutzung von Format findet man z.B. in der offiziellen Dokumentation:

https://docs.python.org/3.1/library/string.html#format-examples

Comprehensions

Sind nützlich, um Listen oder Dictionaries umzuwandeln oder zu erzeugen:

In [84]:
# Ursprüngliche Liste
list(range(5))
Out[84]:
[0, 1, 2, 3, 4]
In [85]:
# List-Comprehension
[2 * x for x in range(5)]
Out[85]:
[0, 2, 4, 6, 8]
In [86]:
# Ursprüngliche Liste
list(enumerate(weekdays))
Out[86]:
[(0, 'Mo'), (1, 'Di'), (2, 'Mi'), (3, 'Do'), (4, 'Fr'), (5, 'Sa'), (6, 'So')]
In [87]:
# Dict-Comprehension
{num + 1: name for num, name in enumerate(weekdays)}
Out[87]:
{1: 'Mo', 2: 'Di', 3: 'Mi', 4: 'Do', 5: 'Fr', 6: 'Sa', 7: 'So'}
In [88]:
# Ursprüngliche Listen
print(list(range(3)))
print(list(range(4)))
[0, 1, 2]
[0, 1, 2, 3]
In [89]:
# List-Comprehension mit verschachtelten Schleifen
[x + y for y in range(3) for x in range(4)]
Out[89]:
[0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5]
In [90]:
# List-Comprehension mit verschachtelten Schleifen und Bedingung
[x + y for y in range(3) for x in range(4) if x % 2 == 0]
Out[90]:
[0, 2, 1, 3, 2, 4]

Häufig auftretende Fehler

Einige Fehler macht man zu Anfang sehr häufig (und immer mal wieder).
Wenn man die Fehlermeldungen richtig zu lesen weiß, kann einem das viel Arbeit/Frust ersparen.
Das Wichtigste ist: Lest die Fehlermeldung und versucht sie zu verstehen!

Gerade am Anfang beliebt: IndentationError:

Die Fehlermeldung gibt die Zeile an in der Fehler auftritt (hier: line 8)
und den Grund für den Fehler, häufig mit Erklärung (hier: IndentationError: expected an indented block)

In [91]:
a = 5
b = 7
c = 9

if a <= b:
    if c >= b:
    # Zeile 8 müsste in dem if-Block eingerückt werden
    print("a <= b und b >= c")
    else:
        print("a <= b und b >= c")
else:
    print("a > b")    
  File "<ipython-input-91-991b56c86fcd>", line 8
    print("a <= b und b >= c")
        ^
IndentationError: expected an indented block

Allgemeiner Syntax-Fehler: SyntaxError

Zeile in der der Fehler auftritt: line 6
Grund für den Fehler: SyntaxError: invalid syntax
Hier sogar mit Hinweis auf das fehlerhafte/fehlende Zeichen.

In [92]:
a = 5
b = 7
c = 9

# In Zeile 6 fehlte der Doppelpunkt ':' nach der Bedingung, um den If-Block einzuleiten
if a <= b
    if c >= b:
        print("a <= b und b >= c")
    else:
        print("a <= b und b >= c")
else:
    print("a > b")
  File "<ipython-input-92-dc232b5fb961>", line 6
    if a <= b
             ^
SyntaxError: invalid syntax

Ein zu Anfang verwirrender Fehler

Die Fehlermeldung ist hier im ersten Moment nicht besonders hilfreich:
Zeile in der der Fehler auftritt: line 11
Grund für den Fehler: SyntaxError: invalid syntax

Stimmt nur zum Teil, denn in Zeile 11 ist alles in Ordnung und der eigentliche Fehler ist in Zeile 10.
In komplexeren Programmen ist das nicht mehr so einfach zu sehen wie hier,
deswegen ist es gut, wenn man dieses Verhalten schon mal gesehen hat.

In [93]:
a = 5
b = 7
c = 9


if a <= b:
    if c >= b:
        # In Zeile 10 fehlt eine Klammer zum Vergleich:
#       d = ((a + b) / (a + c))/((a + b) / (a + c)) 
        d = ((a + b) / (a + c)/((a + b) / (a + c))
        print("a <= b und b >= c")
    else:
        print("a <= b und b >= c")
else:
    print("a > b") 
  File "<ipython-input-93-459af7326e90>", line 11
    print("a <= b und b >= c")
        ^
SyntaxError: invalid syntax

Auch sehr einfach auszulösen: NameError:

Zeile in der der Fehler auftritt: ----> 7 if a <= n:
Grund für den Fehler: NameError: name 'n' is not defined
Fehlermeldung sieht etwas anders aus, liefert aber immer noch die gleichen Informationen.

In [94]:
a = 5
b = 7
c = 9

# Beim Schreiben des Codes vertippt (n anstelle von b),
# sodass a <= n statt a <= b ausgewertet wird, n ist jedoch nicht definiert
if a <= n:
    if c >= b:
        print("a <= b und b >= c")
    else:
        print("a <= b und b >= c")
else:
    print("a > b")    
a > b

Fehler bei Zugriffen auf Listen: IndexError

Zeile in der der Fehler auftritt: ----> 4 names[4]
Grund für den Fehler: IndexError: list index out of range
Fehlermeldung sieht etwas anders aus, liefert aber immer noch die gleichen Informationen.

In [95]:
names = ["Anna","Jan","Marie","Tim"]
# Die Liste hat vier Einträge, da der erste Index aber 0 ist ist der letzte Index 3
# der Index 4 der in Zeile 4 abgerufen wird überschreitet diesen Bereich
names[4]
--------------------------------------------------------------------------
IndexError                               Traceback (most recent call last)
<ipython-input-95-7117deb7d493> in <module>()
      2 # Die Liste hat vier Einträge, da der erste Index aber 0 ist ist der letzte Index 3
      3 # der Index 4 der in Zeile 4 abgerufen wird überschreitet diesen Bereich
----> 4 names[4]

IndexError: list index out of range

Fehler bei Zugriffen auf Dictionaries: KeyError

Zeile in der der Fehler auftritt: ----> 4 numbers["five"]
Grund für den Fehler: KeyError: 'five'
Fehlermeldung sieht etwas anders aus, liefert aber immer noch die gleichen Informationen.

In [96]:
numbers = {"one": 1 ,"two": 2,"three": 3,"four": 4}
# Analog zum Beispiel mit der Listen Index
# Der key "five" ist nicht in dem Dictionary vorhanden
numbers["five"]
--------------------------------------------------------------------------
KeyError                                 Traceback (most recent call last)
<ipython-input-96-d8ed387ded24> in <module>()
      2 # Analog zum Beispiel mit der Listen Index
      3 # Der key "five" ist nicht in dem Dictionary vorhanden
----> 4 numbers["five"]

KeyError: 'five'

Komplexer Fehler mit Traceback:

Wenn an einem Fehler mehrere Funktionen beteiligt sind,
gibt die Fehlermeldung einen Traceback aus.
Dieser Fall ist eher die Regel, vorallem, wenn man Module verwendet.
Der Traceback zeigt die Reihenfolge aller Funktionsaufrufe, die
am Ende zu dem Fehler geführt haben mit dem letzten Aufruf ganz unten (most recent call last).

Zeilen die zum Auftreten des Fehlers führen:
Funktionsaufruf in Zeile 10:
---> 10 add_one_to_inverse_difference(x,y)
Ergebnis des Funktionsaufrufs ist das return in Zeile 5 mit neuem Funktionsaufruf:
----> 5 return 1 + inverse_difference(a, b)
Ergebnis des 2. Funktionsaufrufs ist das return in Zeile 2:
----> 2 return 1/(a - b)
Hier tritt der Fehler auf, da durch Null geteilt wird, wenn a und b gleich sind!

Grund für den Fehler: ZeroDivisionError: division by zero

In [97]:
def inverse_difference(a, b):
    return 1/(a - b)

def add_one_to_inverse_difference(a, b):
    return 1 + inverse_difference(a, b) 

def add_2_to_inverse_difference(a, b):
    return add_one_to_inverse_difference(a, b) + 2

x = 2
y = 2

add_one_to_inverse_difference(x,y)
--------------------------------------------------------------------------
ZeroDivisionError                        Traceback (most recent call last)
<ipython-input-97-1ab54262f343> in <module>()
     11 y = 2
     12 
---> 13 add_one_to_inverse_difference(x,y)

<ipython-input-97-1ab54262f343> in add_one_to_inverse_difference(a, b)
      3 
      4 def add_one_to_inverse_difference(a, b):
----> 5     return 1 + inverse_difference(a, b)
      6 
      7 def add_2_to_inverse_difference(a, b):

<ipython-input-97-1ab54262f343> in inverse_difference(a, b)
      1 def inverse_difference(a, b):
----> 2     return 1/(a - b)
      3 
      4 def add_one_to_inverse_difference(a, b):
      5     return 1 + inverse_difference(a, b)

ZeroDivisionError: division by zero