Funktionen
Bei komplexeren Fragestellungen zeigen sich oft zwei Muster: zum einen benötigt man die gleiche Funktionalität an mehreren Stellen im Programmcode (z.B. ein Skalarprodukt) und zum anderen wird der Code lang und daher unübersichtlich. Beide Probleme lassen sich mit Funktionen lösen. Funktionen sind fast analog zu mathematischen Funktion denkbar. Eine Funktion ist ein Block von Code, der mit einem Satz an Argumenten aufgerufen werden kann. Damit kann die gleiche Funktion mehrfach von verschiedenen Code-Segmenten (und anderen Funktionen) aufgerufen werden.
def print_largest(a, b):
if a > b:
print(a)
else:
print(b)
print_largest(1, 2) # 2`
Funktionen können auch Werte zurückgeben:
def add(a, b):
return a + b
add(1, 2) # 3
Das unterstützt auch die Wartbarkeit: ist eine Logik fehlerhaft, so muss bei Verwendung von Funktionen nur diese eine Stelle angepasst werden, statt jedes Vorkommen in einer potentiell großen Codebasis. Zusätzlich entlastet die Funktion durch Reduktion der mentalen Komplexität: anstatt den gesamten Code zu verstehen, reicht es, die Funktion zu verstehen.
Der Unterschied zur mathematischen Funktion ist, dass eine Funktion in Python auch Nebeneffekte haben kann, wie z.B. etwas auf dem Bildschirm ausgeben oder eine Datei schreiben. Funktionen, die keine Nebeneffekte haben, sondern nur Berechnungen durchführen werden pure functions genannt, was später im Kontext von JAX noch relevant sein wird.
Verkettungen und Aufrufe
Funktionen lassen sich verketten, indem die Ausdrücke verschachtelt genutzt werden:
def add(a, b):
return a + b
add(add(1, 2), 3) # 6
Eine sinnvolle Einheit für eine Funktion ist entweder logischer Natur (die Funktion löst also ein in sich geschlossenes Problem) oder aber praktischer Natur (die Funktion löst ein oft vorkommendes Teilproblem). Ein Beispiel ist
def add(a, b):
return a + b
def list_sum(list_to_sum):
total = 0
for element in list_to_sum:
total = add(total, element)
return total
Implizite Tupel und automatisches Entpacken
Soll eine Funktion mehr als einen Wert zurückgeben, kann man dem return
-Statement mehrere komma-getrennte Werte übergeben. Diese werden dann implizit in ein Tupel konvertiert:
def min_max(list_to_analyze):
min_value = list_to_analyze[0]
max_value = list_to_analyze[0]
for element in list_to_analyze:
if element < min_value:
min_value = element
if element > max_value:
max_value = element
return min_value, max_value
min_max([1, 2, 3, 4, 5]) # (1, 5)
Das Tupel kann dann auch automatisch entpackt werden:
min_value, max_value = min_max([1, 2, 3, 4, 5])
Funktionen als Variablen
Funktionen lassen sich wie Variablen behandeln und weitergeben. Das kann zu eleganten Programmstrukturen führen:
def add(a, b):
return a + b
def subtract(a, b):
return a - b
def apply_operation(operation, a, b):
return operation(a, b)
apply_operation(add, 1, 2) # 3
apply_operation(subtract, 1, 2) # -1