Python

  • Aktuelle Version: Python 3.11
  • Interpretierte Programmiersprache
  • Kein Kompilieren
  • Programme werden mit dem python-Programm ausgeführt
  • Eignet sich sehr gut zum Erlernen der Programmierung!
  • Viele nützliche Pakete, die das wissenschaftliche Arbeiten extrem erleichtern!

Hier soll es hingehen: Auswertung von Praktikumsversuchen:

In [1]:
from IPython.display import Image

Image(filename="build/muon_plot.png")
Out[1]:
No description has been provided for this image

Versuch aus dem Fortgeschrittenen-Praktikum: Lebensdauer kosmischer Myonen

In [2]:
%%javascript
$.getScript('https://kmahelona.github.io/ipython_notebook_goodies/ipython_notebook_toc.js')

Inhalt

Grundlagen

Eine Programmiersprache wie Python lässt sich nicht an einem Tag lernen. Wir geben hier 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 [3]:
print("Hello, World!")
Hello, World!

Kommentare

  • Sehr wichtig für die Leute, die mit euch arbeiten
  • Auch für euch selbst zwei Wochen später

Alles was in Python hinter einem # steht, wird nicht als Code betrachtet und dient als Kommentar. Längere Kommentare werden mit drei Anführungszeichen begonnen und mit drei weiteren begonnen.

In [4]:
# We start the workshop with the traditional first program
print("Hello, World!")
"""
This example is chosen to show the basic functionalities of python.
"""
Hello, World!
Out[4]:
'\nThis example is chosen to show the basic functionalities of python.\n'

Kommentare sollten nicht erklären, was der Code tut, sondern warum. Wie jeder andere Code, sollten Kommentare auf Englisch sein.

Variablen

Man kann einem Objekt einen Namen geben:

In [5]:
a = 2
b = 3

Variablennamen können auch überschrieben werden:

In [6]:
a = 42
b = a

a = 0
b
Out[6]:
42
  • Namen verweisen auf Objekte
  • Mehrere Namen können auf das gleiche Objekt zeigen

Datentypen

Einfache Datentypen

Booleans

True und False

Logische Operatoren: and, or, not

In [7]:
True and False
Out[7]:
False
In [8]:
True or False
Out[8]:
True
In [9]:
not True
Out[9]:
False

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

In [10]:
"" or True
Out[10]:
True
In [11]:
"Hallo" and True
Out[11]:
True

Zahlen

Ganze Zahlen: 42
Kommazahlen: 3.14
Komplexe Zahlen: 3 + 2j

Rechenoperatoren:

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

Rechenoperationen können geklammert werden

In [13]:
a = 2 + 4 / 5 + 1
b = (2 + 4) / 5 + 1
c = (2 + 4) / (5 + 1)
In [14]:
print(a, b, c)
3.8 2.2 1.0
In [15]:
a = 3 + 2j
b = 1 + 1j

print(a + b, a * b, abs(b))
(4+3j) (1+5j) 1.4142135623730951

Vergleichsoperatoren geben True oder False zurück

  • ==, !=
  • >, <, >=, <=
In [16]:
x = 5
y = 3
x < y
Out[16]:
False

Mehrere Vergleichsoperatoren können in einem Ausdruck stehen:

In [17]:
a = 3.8
b = 2.2
c = 1
c < b < a
Out[17]:
True
In [18]:
c < a < b
Out[18]:
False

Strings

Stehen in ' oder ". (Für eins entscheiden)

In [19]:
foo = "foo"
bar = "bar"

Strings können mit + konkateniert werden:

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

Strings können mit * vervielfacht werden

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

Strings können mit [] indiziert werden

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

Können beliebige Unicode Symbole enthalten

In [23]:
print("π ist genau 3, Erwin Schrödinger")
π ist genau 3, Erwin Schrödinger

None

Stellt das Fehlen eines Wertes dar.

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

In [24]:
print(None)
None

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

In [25]:
print("test")
test
In [26]:
a = print("test")
test
In [27]:
print(a)
None

Sammlungen (Collections)

Speichern mehrerer Objekte

Listen

  • Veränderbar
  • Gedacht für mehrere Werte vom selben Typ
In [28]:
names = ["foo", "bar"]

Listen können mit [] indiziert werdern

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

Man kann Werte an Listen anhängen:

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

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

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

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

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

Listen werden mit + zusammengeführt:

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

Listen können mit * vervielfacht werden:

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

Die Liste kann auch erweitert werden:

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

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

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

Mit in wird überprüft ob ein Wert in der Liste ist:

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

Tupel

  • Gedacht für Werte mit unterschiedlichen Typen
  • Werden an allen möglichen Stellen von python erzeugt
In [38]:
tup = (5, 3)
tup
Out[38]:
(5, 3)

Können wahlweise auch ohne Klammern geschrieben werden (wenn es eindeutig ist).

In [39]:
tup = 5, 3
tup
Out[39]:
(5, 3)
In [40]:
a = 5
b = 3
a, b = b, a
In [41]:
print("a =", a)
print("b =", b)
a = 3
b = 5
In [42]:
tup[0]
Out[42]:
5

Einträge nach Erzeugung unveränderbar:

In [43]:
tup[1] = 7
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[43], line 1
----> 1 tup[1] = 7

TypeError: 'tuple' object does not support item assignment

Dictionaries

In [44]:
numbers = {"one": 1, "two": 2, "three": 3}
In [45]:
numbers["two"]
Out[45]:
2

Dictionaries sind extrem hilfreich, zum Beispiel um die verschiedene Datensätze der gleichen Messung zusammenzufassen. So wird es einfacher dieselbe Analyse auf die einzelnen Datensätze anzuwenden.

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

Leere Dictionaries werden mit der Funktion dict()erstellt.

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

Kontrollstrukturen

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

Sogennante Kontrollstrukturen erlauben es, den Ablauf zu steuern.

if / elif / else

In [51]:
a = 3

if a == 1:
    # muss eingerückt werden, 4 spaces:
    print("foo")
elif a == 2:
    print("bar")
else:
    print("baz")
baz

while

Wiederholen, solange eine Bedingung erfüllt ist.

In [52]:
i = 0
while i < 5:
    print(i)
    i += 1

# there has to be a better way!
0
1
2
3
4

for

Wiederholen für jedes Elements (z.B. einer Liste)

In [53]:
data = [10, 42, -1]

for x in data:
    # Wir können auf das jetzige Element als "x" zugreifen
    print(2 * x)
20
84
-2

klassische for-Schleife mit range:

In [54]:
for i in range(5):
    print(i)
0
1
2
3
4
In [55]:
for i in range(2, 5):
    print(i)
2
3
4
In [56]:
for i in range(2, 10, 2):
    print(i)
2
4
6
8
In [57]:
for i in range(10, 3, -1):
    print(i)
10
9
8
7
6
5
4
In [58]:
weekdays = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
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 [59]:
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 [60]:
list(enumerate(weekdays))
Out[60]:
[(0, 'Mo'), (1, 'Di'), (2, 'Mi'), (3, 'Do'), (4, 'Fr'), (5, 'Sa'), (6, 'So')]
In [61]:
voltages = [1, 3, 5]
currents = [1, 5, 1]

for voltage, current in zip(voltages, currents):
    print("R =", voltage / current)
R = 1.0
R = 0.6
R = 5.0

Was zip() macht:

In [62]:
list(zip(voltages, currents))
Out[62]:
[(1, 1), (3, 5), (5, 1)]

Iterieren über die Einträge von dict

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

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

Übungen 1, 2, und 3 können jetzt bearbeitet werden.

Funktionen

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

In [64]:
print("Hello!")
Hello!
In [65]:
len([1, 2, 3, 4, 5])
Out[65]:
5

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

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

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

In [67]:
print(1, 2, 3, 4, 5, sep=", ")
1, 2, 3, 4, 5

Objekte haben auch Funktionen (diese nennt man Methoden):

In [68]:
s = "test"
s.upper()
Out[68]:
'TEST'
In [69]:
"foo bar baz".split()
Out[69]:
['foo', 'bar', 'baz']

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

In [70]:
print?

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

Eigene Funktionen definieren mit def

Um mehrfach benötigten Code einfacher zu nutzen, können Funktionen definiert werden.

Vorteile:

  • Weniger Code
  • Änderungen nur an einer Stelle notwending
In [71]:
def add(x, y):
    z = x + y
    return z


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

Mit dem return Statement wird die Funktion beendet und Ergebnisse zurückgeben.

Es können auch mehrere Werte zurückgeben werden:

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


n, rest = divide(5, 3)
n, rest
Out[72]:
(1, 2)

Funktionen können sich selbst aufrufen (Rekursion)

In [73]:
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)


factorial(4)
Out[73]:
24

Module und import

In Python kann man Code in einzelne Module portionieren. Es stehen bereits unglaublich viele Module für Python zur Verfügung. Und gerade für das wissenschaftliche Arbeiten ist eigentlich alles da, was gebraucht wird und noch viel mehr.

Wird etwas aus einem anderen Modul gebraucht, importiert man es.

Zahlreiche Module sind Teil jeder Python Installation, die sogenannte "standard library": https://docs.python.org/3/library/

Weitere Module hast du in deiner toolbox Umgebung mit Mamba installiert.

In [74]:
import os

os.listdir(".")
Out[74]:
['Makefile',
 'build',
 'matplotlibrc_muon',
 'matplotlib.ipynb',
 'test.txt',
 'numeric-python.ipynb',
 'python.ipynb',
 'muon_plot.py',
 'header-matplotlib.tex',
 'uncertainties.ipynb',
 'plot_1.pdf',
 'scientific-python.ipynb',
 'data',
 'images',
 'plot_2.pdf',
 '.gitignore',
 'README.md',
 'plot.pdf']

Für das Importieren einzelner Funktionen gibt es den Befehl:

In [75]:
from time import sleep

print("hello")
sleep(1)
print("world")
hello
world

Den Modulen können mit as neue Namen gegeben werden Viele Module haben jedoch Konventionen für den Kurznamen, die du verwenden solltest.

In [76]:
import numpy as np

np.sum([1, 2])  # siehe numpy Kurs
Out[76]:
3

Auslagern in Python-Dateien

  • Python-Dateien haben die Endung .py
  • Ausführen mit python programm.py
  • Einzelne Python-Dateien (Endung .py) kann man als Module benutzen.
    Liegt eine Datei test.py im selben Ordner, so kann der Inhalt mit import test in einer weiteren Datei verwendet werden.
  • Die Datei muss mit dem Encoding UTF-8 abgespeichert werden. (Standard in modernen Editoren / Betriebssystemen)

Es folgt ein fertiges Python-Programm.

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

In [77]:
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]

Übung 4, 5 und 6 können bearbeitet werden.

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 [78]:
U = 42
R = 7

print(f"Spannung U = {U}, R = {R}, I = {U / R}")
Spannung U = 42, R = 7, I = 6.0

Der Ausdruck in den {}-Markierungen wird ausgewertet und das Ergebnis anstelle der geschweiften Klammern gesetzt.
Variablen ersetzt.

f-strings haben viele fortgeschrittene Funktionen. Beispielsweise kann die gewünschte Genauigkeit für floats angeben werden:

In [79]:
result = 3.2291421

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

Alle Formateinstellungen werden nach einem Doppelpunkt angegeben.
Für die Genauigkeit sieht die Angabe allgemein so aus: :.Nf
N ist dabei die gewünschte Anzahl an Nachkommastellen.

Sonderzeichen

Mit \ kann man in Strings besondere Anweisungen setzen:

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

Wenn viele '\' geschrieben werden müssen (z.B. in LaTeX-Code), lohnt es sich diese Funktion mit dem Prefix r auszuschalten:

In [80]:
print(r"\Huge\texttt{Python}")
\Huge\texttt{Python}

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

LaTeX sieht dann leider so aus:

In [81]:
energy = 1.23456
print(rf"\qty{{{energy:.4f}}}{{\kilo\joule}}")
\qty{1.2346}{\kilo\joule}

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

In [82]:
print(rf"\qty {{ {energy:.4f} }} {{ \kilo\joule }}")
\qty { 1.2346 } { \kilo\joule }

Comprehensions

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

In [83]:
# Ursprüngliche Liste
list(range(5))
Out[83]:
[0, 1, 2, 3, 4]
In [84]:
# List-Comprehension
[2 * x for x in range(5)]
Out[84]:
[0, 2, 4, 6, 8]
In [85]:
# List-Comprehension mit Bedingung
[2 * x for x in range(5) if x % 2 == 0]
Out[85]:
[0, 4, 8]
In [86]:
# Ursprüngliche Liste
weekdays = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "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]

Übung 7 kann jetzt bearbeitet werden.

Häufig auftretende Fehler

Einige Fehler werden zu Anfang sehr häufig gemacht (und immer mal wieder).
Wenn man die Fehlermeldungen richtig zu lesen weiß, kann einem das viel Arbeit/Frust ersparen.
Das Wichtigste ist: Lies die Fehlermeldung und versuche 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 c >= b")
    else:
        print("a <= b und b > c")
else:
    print("a > b")    
  Cell In[91], line 8
    print("a <= b und c >= b")
    ^
IndentationError: expected an indented block after 'if' statement on line 6

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 c >= b")
    else:
        print("a <= b und b > c")
else:
    print("a > b")
  Cell In[92], line 6
    if a <= b
             ^
SyntaxError: expected ':'

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 c >= b")
    else:
        print("a <= b und b > c")
else:
    print("a > b") 
  Cell In[93], line 10
    d = ((a + b) / (a + c)/((a + b) / (a + c))
        ^
SyntaxError: '(' was never closed

Auch sehr einfach auszulösen: NameError:

Zeile in der der Fehler auftritt: ----> 8 if a <= n:
Grund für den Fehler: NameError: name 'n' is not defined Wir können keine Variable verwenden, die nicht existiert.

In [94]:
del n  # Please ignore me, I'm just setting the stage for the example
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 c >= b")
    else:
        print("a <= b und b > c")
else:
    print("a > b")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[94], line 8
      4 c = 9
      6 # Beim Schreiben des Codes vertippt (n anstelle von b),
      7 # sodass a <= n statt a <= b ausgewertet wird, n ist jedoch nicht definiert
----> 8 if a <= n:
      9     if c >= b:
     10         print("a <= b und c >= b")

NameError: name 'n' is not defined

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

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)
Cell In[95], line 4
      1 names = ["Anna", "Jan", "Marie", "Tim"]
      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'

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)
Cell In[96], line 4
      1 numbers = {"one": 1, "two": 2, "three": 3, "four": 4}
      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 Module verwendet werden. 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 13:
---> 13 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)
Cell In[97], line 16
     13 x = 2
     14 y = 2
---> 16 add_one_to_inverse_difference(x, y)

Cell In[97], line 6, in add_one_to_inverse_difference(a, b)
      5 def add_one_to_inverse_difference(a, b):
----> 6     return 1 + inverse_difference(a, b)

Cell In[97], line 2, in inverse_difference(a, b)
      1 def inverse_difference(a, b):
----> 2     return 1 / (a - b)

ZeroDivisionError: division by zero

Debugging leicht gemacht

Es kann vorkommen, dass man mal überhaupt nicht weiß, warum etwas nicht funktioniert.

In diesem Fall sollte man zunächst mal Ruhe bewahren und sich dann auf systematische Fehlersuche begeben.
Ein einfacher Ansatz ist das print()-debugging.

Kleiner Trick, um Schuhgröße und Alter einer weiteren Person zu berechnen. Die zweite Person muss folgende Anweisungen befolgen:

1. Multipliziere deine Schuhgröße mit 5         
2. Addiere 50 zum Ergebnis                      
3. Multipliziere das Ergebnis mit 20            
4. Addiere 1020 zum Ergebnis                    
5. Ziehe dein Geburtsjahr vom Ergebnis ab
6. Nenne das Ergebnis
In [98]:
def shoesize_and_age(shoesize, birth_year):
    step_1 = shoesize * 5
    step_2 = step_1 + 50
    step_3 = step_2 * 20
    step_4 = step_3 + 1020
    step_5 = step_4 - birth_year

    return step_5

Beispiel:

Mit der Schuhgröße 45 und dem Geburtsjahr 1993, kriege ich die Zahl 4527 raus.
Ich habe (wie erwähnt die Schuhgröße 45) und bin 27 Jahre alt.
Wie funktioniert dieser Trick?!

In [99]:
shoesize_and_age(45, 1993)
Out[99]:
4527

Die print()-Debugguing Methode:

In [100]:
def shoesize_and_age(shoesize, birth_year):
    print("shoesize:", shoesize)
    step_1 = shoesize * 5
    print("step_1:", step_1)
    step_2 = step_1 + 50
    print("step_2:", step_2)
    step_3 = step_2 * 20
    print("step_3:", step_3)
    step_4 = step_3 + 1020
    print("step_4:", step_4)
    step_5 = step_4 - birth_year
    print("step_5:", step_5)

    return step_5
In [101]:
shoesize_and_age(45, 1993)
shoesize: 45
step_1: 225
step_2: 275
step_3: 5500
step_4: 6520
step_5: 4527
Out[101]:
4527

Die coolere IPython.embed()-Methode:

In [102]:
import IPython


def shoesize_and_age(shoesize, birth_year):
    step_1 = shoesize * 5
    step_2 = step_1 + 50
    step_3 = step_2 * 20
    step_4 = step_3 + 1020
    step_5 = step_4 - birth_year

    IPython.embed()

    return step_5
In [103]:
shoesize_and_age(45, 1993)
Python 3.11.5 | packaged by conda-forge | (main, Aug 27 2023, 03:34:09) [GCC 12.3.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.15.0 -- An enhanced Interactive Python. Type '?' for help.

---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
File /tmp/ipykernel_23014/2333610718.py:1
----> 1 shoesize_and_age(45, 1993)

File /tmp/ipykernel_23014/2372780137.py:11, in shoesize_and_age(shoesize, birth_year)
      8 step_4 = step_3 + 1020
      9 step_5 = step_4 - birth_year
---> 11 IPython.embed()
     13 return step_5

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/IPython/terminal/embed.py:409, in embed(header, compile_flags, **kwargs)
    406 frame = sys._getframe(1)
    407 shell = InteractiveShellEmbed.instance(_init_location_id='%s:%s' % (
    408     frame.f_code.co_filename, frame.f_lineno), **kwargs)
--> 409 shell(header=header, stack_depth=2, compile_flags=compile_flags,
    410     _call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno))
    411 InteractiveShellEmbed.clear_instance()
    412 #restore previous instance

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/IPython/terminal/embed.py:245, in InteractiveShellEmbed.__call__(self, header, local_ns, module, dummy, stack_depth, compile_flags, **kw)
    241     self.show_banner()
    243 # Call the embedding code with a stack depth of 1 so it can skip over
    244 # our call and get the original caller's namespaces.
--> 245 self.mainloop(
    246     local_ns, module, stack_depth=stack_depth, compile_flags=compile_flags
    247 )
    249 self.banner2 = self.old_banner2
    251 if self.exit_msg is not None:

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/IPython/terminal/embed.py:337, in InteractiveShellEmbed.mainloop(self, local_ns, module, stack_depth, compile_flags)
    334 self.set_completer_frame()
    336 with self.builtin_trap, self.display_trap:
--> 337     self.interact()
    339 # now, purge out the local namespace of IPython's hidden variables.
    340 if local_ns is not None:

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/IPython/terminal/interactiveshell.py:873, in TerminalInteractiveShell.interact(self)
    870 print(self.separate_in, end='')
    872 try:
--> 873     code = self.prompt_for_code()
    874 except EOFError:
    875     if (not self.confirm_exit) \
    876             or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/IPython/terminal/interactiveshell.py:615, in TerminalInteractiveShell.init_prompt_toolkit_cli.<locals>.prompt()
    613 def prompt():
    614     prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
--> 615     lines = [input(prompt_text)]
    616     prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
    617     while self.check_complete('\n'.join(lines))[0] == 'incomplete':

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/IPython/utils/py3compat.py:48, in input(prompt)
     47 def input(prompt=""):
---> 48     return builtin_mod.input(prompt)

File ~/.local/mambaforge/envs/toolbox/lib/python3.11/site-packages/ipykernel/kernelbase.py:1201, in Kernel.raw_input(self, prompt)
   1199 if not self._allow_stdin:
   1200     msg = "raw_input was called, but this frontend does not support input requests."
-> 1201     raise StdinNotImplementedError(msg)
   1202 return self._input_request(
   1203     str(prompt),
   1204     self._parent_ident["shell"],
   1205     self.get_parent("shell"),
   1206     password=False,
   1207 )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Das funktioniert auch in normalen Python .py Dateien.
IPython.embed() öffnet ein IPython-Terminalfenster mit allen Variablennamen, die vorher definiert wurden. Man kann dann interaktiv nach Problemen im Code suchen oder wie hier herausfinden wie Code funktioniert.