Glossar

Wähle eines der Schlüsselwörter auf der linken Seite…

Programmieren mit PythonFunktionen

Lesezeit: ~25 min

Funktionen können verwendet werden, um Code zu organisieren und Aufgaben auszulagern: Sobald eine Funktion geschrieben ist, kann man sich darauf verlassen, dass sie ihre vorgesehene Aufgabe erfüllt, ohne dass der Programmierer darüber nachdenken muss, wie sie das macht. Diese konzeptionelle Hilfe ist entscheidend für das Schreiben von wartbarem Code zur Lösung großer, komplexer Probleme.

Eine gute Faustregel ist, dass eine Funktion ausreichend allgemein sein sollte, um wiederverwendbar zu sein, ohne schon vorhandene Logik zu duplizieren, aber auch so speziell, dass man sie wirklich einsetzen kann.

Übung
Wie könnte das Design des folgenden Codes verbessert werden?

def loesche_ein_anfangs_leerzeichen(S):
    if S[0] == " ":
        return S[1:]
    else:
        return S

def loesche_zwei_anfangs_leerzeichen(S):
    if S[0:2] == "  ":
        return S[2:]
    else:
        return S

def loesche_drei_anfangs_leerzeichen(S):
    if S[0:3] == "  ":
        return S[3:]
    else:
        return S

Lösung. Wir sollten eine einzige Funktion haben, um die Anzahl der überflüssigen Leerzeichen am Anfang eines Strings zu entfernen. Das obige Design hat das Problem, dass wir herausfinden müssen, wie viele führende Leerzeichen es gibt, bevor wir die entsprechende Funktion aufrufen können, was bedeutet, dass die meiste Arbeit, die eigentlich von der Funktion ausgeführt werden sollte, schon vor dem Aufruf der Funktion ausgeführt werden muss. Dadurch wird die Auslagerung der Arbeit nicht umgesetzt.

Argumente

Die Objekte, die einer Funktion beim Aufruf übergeben werden, werden als Argumente der Funktion bezeichnet. Die Variablen, die die Argumente in der Funktionsdefinition darstellen, werden Parameter genannt. Der eingerückte Codeblock, der beim Aufruf der Funktion ausgeführt wird, ist der Body der Funktion (Funktionsrumpf).

Übung
Im folgenden Codeblock ist s ein , während "hello" ein ist.

def dupliziere(s):
    return s + s

dupliziere("hello")

Wir können Parametern Standardwerte zuweisen und für diese Parameter die Argumente dann optional (nicht zwingend) beim Aufruf der Funktion angeben.

 
def gerade(k, x, d=0):
    return k * x + d

gerade(2,3) # ergibt 6
gerade(5,4,d=2) # ergibt 22

Die Argumente 2, 3, 4 und 5 in diesem Beispiel werden Positionsargumente genannt, und b=2 ist ein Schlüsselwort-Argument.

Wenn der Funktionsteil mit einem String-Literal beginnt, wird dieser String als Dokumentation für die Funktion interpretiert. Dieser DocString hilft dir und anderen Benutzern deiner Funktionen, schnell festzustellen, wie sie verwendet werden sollen. Auf die Dokumentation einer Funktion kann in einer Python-Sitzung über die Funktion helpzugegriffen werden. Beispielsweise gibt help(print) den DocString für die eingebaute print-Funktion aus.

Anonyme Funktionen

Eine Funktion kann definiert werden, ohne ihr einen Namen zu geben. Eine solche Funktion gilt als anonym. Die Python-Syntax für anonyme Funktionen verwendet das Schlüsselwort lambda. Eine häufige Situation, in der anonyme Funktionen nützlich sein können, ist die Bereitstellung einer Funktion an eine andere als Argument. Zum Beispiel:

def mache_drei_mal(f, x):
    return f(f(f(x)))

mache_drei_mal(lambda x: x*x, 2)

Eine Funktion mit mehreren Argumenten funktioniert ähnlich, wobei die Parameter durch Kommas getrennt sind: Der Additionsoperator + könnte folgendermaßen geschrieben werden lambda x,y: x + y.

Übung
Schreibe eine Funktion, die zwei Argumente a und b und eine Funktion f übernimmt und a zurückgibt, wenn f(a) < f(b) und b ansonsten. Verwende dann die anonyme Funktionssyntax, um deine Funktion mit zwei Zahlen und der Negationsfunktion x\mapsto -x aufzurufen.

 

Lösung. Eine mögliche Lösung:

def which_smaller(a, b, f):
    if f(a) < f(b):
        return a
    else:
        return b

which_smaller(4, 6, lambda x: -x)

Gültigkeitsbereich

Der Gültigkeitsbereich einer Variablen ist der Bereich im Programm, in man sie aufrufen kann. Wenn du beispielsweise x in Zeile 413 deiner Datei so definierst, dass es auf 47 gesetzt wird und einen Fehler erhältst, weil du versucht hast, x in Zeile 35 zu verwenden, dann besteht das Problem darin, dass die Variable hier noch nicht gültig ist.

Eine Variable, die im Hauptteil (main body) einer Datei definiert ist, hat globalen Gültigkeitsbereich, was bedeutet, dass sie von ihrem Definitionspunkt aus im gesamten Programm sichtbar ist.

Eine im Funktionsrumpf definierte Variable befindet sich im lokalen Gültigkeitsbereich dieser Funktion. Zum Beispiel:

def f(x):
    y = 2
    return x + y

y

Übung
Versuche, eine Funktionsdefinition in eine andere zu verschachteln. Sind Variablen im äußeren Funktionsrumpf in der inneren Funktion verfügbar? Wie ist das im umgekehrten Fall?

def f():
    def g():
        j = 2
        return i
    print(j)
    i = 1
    return g()

f()

Lösung. Die in der inneren Funktion definierte Variable ist nicht im Gültigkeitsbereich des Funktionsrumpfs der äußeren Funktion, aber die im Funktionsrumpf der äußeren Funktion definierte Variable ist im Gültigkeitsbereich der inneren Funktion.

Testen

Es wird dringend empfohlen, Tests zu deinen Funktionen zu schreiben und zur Verfügung zu stellen. Damit wird bestätigt, dass sich jede Funktion wie erwartet verhält. Dies ist besonders wichtig, wenn deine Codebasis immer größer wird, da Änderungen in einer Funktion zu Problemen in anderen Funktionen führen können, die sie verwenden. Eine Möglichkeit zum Testen der Funktionen in deiner gesamten Codebasis hilft dir dabei, diese Fehler schnell zu entdecken, bevor sie zu Schäden führen.

Eine gängige Methode dazu (die du in diesem Kurs bereits mehrfach gesehen hast) ist das Schreiben von Funktionen, deren Namen mit test_ beginnen und die assert-Anweisungen enthalten. Eine assert-Anweisung löst einen Fehler aus, wenn der darauf folgende Ausdruck False zurück gibt. Man kann die Testfunktionen direkt ausführen, oder man kann ein Tool wie pytest verwenden, um alle Testfunktionen in der Codebasis zu finden und auszuführen.

def space_concat(s,t):
    """
    Verkettung der Strings s und t, wobei ein Leerzeichen
    zwischen ihnen eingefügt wird, wenn s mit einem Nicht-Leerzeichen endet
    und t mit einem Nicht-Leerzeichen beginnt
    """
    if s[-1] == " " or t[0] == " ":
        return s + t
    else:
        return s + " " + t

def test_space_concat():
    assert space_concat("foo", "bar") == "foo bar"
    assert space_concat("foo ", "bar") == "foo bar"

test_space_concat()
space_concat("foo", "bar")

Übung
Die obigen Testfälle decken nicht die ungünstige Situation ab, dass einer der Strings leer ist. Liefert die Funktion korrekte Werte für diese ungünstigen Fälle? Füge dafür Testfälle hinzu und verbessere die Funktion so, dass diese bestanden werden.

Lösung. Wir überprüfen ob die Strings leer sind bevor wir die letzten bzw. ersten Zeichen überprüfen. Mit or kann das Problem schnell gelöst werden, weil wenn der erste bool-Wert in einer or Operation True ist, wird der zweite erst gar nicht ausgewertet.

def space_concat(s,t):
    """
    Verkettung der Strings s und t, wobei ein Leerzeichen
    zwischen ihnen eingefügt wird, wenn s mit einem Nicht-Leerzeichen endet
    und t mit einem Nicht-Leerzeichen beginnt.
    """
    if s == "" or t == "" or s[-1] == " " or t[0] == " ":
        return s + t
    else:
        return s + " " + t

def test_space_concat():
    assert space_concat("foo", "bar") == "foo bar"
    assert space_concat("foo ", "bar") == "foo bar"
    assert space_concat("foo", "") == "foo"
    assert space_concat("", "bar") == "bar"

Übungen

Übung
Schreibe eine Funktion, die zwei Strings als Eingabe akzeptiert und die Verkettung dieser beiden Strings in alphabetischer Reihenfolge zurückgibt.

Tipp: Was meinst du, mit welchem Operator Strings alphabetisch verglichen werden können.

def alphabetical_concat(s,t):
    pass # hier Code einfügen

def test_concat(): 
    assert alphabetical_concat("buchstaben", "suppe") == "buchstabensuppe"
    assert alphabetical_concat("socken", "rote") == "rotesocken"
    return "Tests erfolgreich bestanden!"

test_concat()

Lösung.

def alphabetical_concat(s,t):
    if s < t:
        return s + t
    else:
        return t + s

def test_concat(): 
    alphabetical_concat("buchstaben", "suppe") == "buchstabensuppe"
    alphabetical_concat("food", "brain") == "brainfood"
    return "Tests passed!"

test_concat()
Bruno
Bruno Bruno