Der Python-Kurs: Funktionen
Willemers Informatik-Ecke
Dictionary Python-Kurs Klassen

Ein wichtiges Prinzip in der Programmierung besagt, dass man möglichst keinen Code mehrfach schreiben soll. Sobald man also Code-Zeilen kopieren möchte, sollte man die entsprechenden Zeilen in einer Funktion aufrufen. Unter Python sähe das so aus.
# zähle bis 10
for i in range(1,11,1):
    print(i)
...
# zähle bis 10
Statt hier die beiden Zeilen erneut einzuippen oder zu kopieren, definieren wir eine Funktion und rufen sie zwei Mal auf.

Definition einer Funktion

def zaehleBis10():
   for i in range(1,11,1):
       print(i)

...
zaehleBis10()
...
zaehleBis10()
Funktionen ermöglichen es, aus mehreren Befehlen einen neuen Befehl zusammenzusetzen. Die Definition der Funktion allein bewirkt keine Aktivität des Programms. Erst wenn die Funktion aufgerufen wird, wird sie aktiv.

Etwas speziell: Funktionsvariablen

Wenn man den Namen einer Funktion ohne Klammern verwendet, wird nicht etwa die Funktion aufgerufen, sondern sie liefert die Stelle, an der die Funktion definiert wurde, also deren Adresse.

Diese Adresse kann wiederum einer anderen Variablen zugewiesen werden, die dann ebenfalls die Adresse der Funktion enthält.

Hängt man dieser Variablen nun ein paar Klammern an, wird die Funktion aufrufen, deren Adresse sie enthält.

Im Augenblick scheint das eher Quatsch zu sein, aber es gibt tatsächlich praktische Anwendungen dafür, beispielsweise, wenn Funktionen als Parameter für einen Rückruf übergeben werden sollen.

def zaehleBis10():
   for i in range(1,11,1):
       print(i)

anders = zaehleBis10
anders() # ruft die Funktion zaehleBis10

Lokale Variablen

In der Funktion zaehleBis10 wird übrigens die Variable i verwendet. Diese Variable bleibt nach außen unsichtbar in der Funktion. Ein Zugriff von außen ist nicht möglich. Man spricht von lokalen Variablen.

Nicht einmal dann, wenn man außerhalb der Funktion eine Variable gleichen Namens verwendet, wird sie durch die Variable in der Funktion verändert.

>>> i = 4
>>> print(i)
4
>>> zaehleBis10()
1
2
...
10
>>> print(i)
4

Parameter

Bisher hat unsere Funktion immer genau bis 10 gezählt. Aber vielleicht will man an der einen Stelle im Programm bis 5 zählen und an der nächsten bis 7. Damit wir deshalb nicht schon wieder überflüssigen Code mehrfach verwenden, ist es möglich, der Funktion mitzuteilen, bis wohin sie zählen soll. Das erreicht man mit einer Parametervariablen, die man zwischen die Klammern setzt.
def zaehle(ende):
   for i in range(1,ende+1,1):
       print(i)
Beim Aufruf wird nun die gewünschte Zahl zwischen die Zähne, sorry - zwischen die Klammern genommen.
zaehle(5)
zaehle(7)
In der ersten Zeile wird die 5 in die Parametervariable ende kopiert. Die Funktion zählt nun also bis 5. Beim zweiten Aufruf kommt eine 7 zwischen die Klammern. Beim Start der Funktion findet die Funktion zaehle also die 7 in der Variablen ende und zählt bis 7.

Was mit festen Zahlen funktioniert, funktioniert natürlich auch mit Variablen. Der folgende Aufruf wird zaehle also bis 8 zählen lassen.

anzahl = 8
zaehle(anzahl)
Die Funktion kann den Parameter ende wie eine Variable verwenden. Hier wird der Parameter als Variable ende an den Aufruf von range() weitergereicht, um das Ende frei gestalten zu können.

Parametertypen prüfen

Parameter können vom Aufrufer frei belegt werden. Dumm nur, wenn statt der erwarteten Zahl ein Text übergeben wird. Python ist das zunächst egal, weil Variablen ja beliebige Typen aufnehmen können. Aber es wird schon etwas schwierig, wenn man bis zu seinem eigenen Vornamen zählen soll.

Um solche Probleme zu verhindern, kann man den Typ einer Variablen mit der Funktion type prüfen.

def zaehle(ende): 
    if type(ende) in (float, int):
        for i in range(1, ende+1, 1):
            print(i)
    else:
        print("nix")
Im unserem Fall wird geprüft, ob der Typ float oder int ist. Damit kann man wunderbar zählen. Falls doch ein Text oder sonst etwas Merkwürdiges übergeben wurde, gibt die Funktion einfach nix aus.
>>> zaehle(3)
1
2
3
>>> zaehle('Hallo')
nix

Vorbelegte Parameter

Sie können die Parameter einer Funktion bei der Definition vorbelegen. Das ist praktisch, wenn bestimmte Werte nur selten verändert werden sollen.

Alle vorbelegten Parameter müssen beim Aufruf nicht zwingend angegeben werden. Sie erhalten dann eben den vorbelegten Wert. Die Funktion zaehle() wird nun so verändert, dass der Schritt beim Zählen als weiterer Paramter angegeben werden kann. Ansonsten wird eine Schrittweite von 1 angenommen.

def zaehle(ende, schritt=1):
   for i in range(1,ende+schritt,schritt):
       print(i)
Der erste Aufruf übergibt lediglich eine 3 und wird darum bis 3 zählen und dabei immer um 1 erhöhen. Im zweiten Aufruf soll bis 5 gezählt werden, aber dieses Mal immer um 2 erhöht werden.
>>> zaehle(3)
1
2
3
>>> zaehle(5, 2)
1
3
5

Parameter per Namen belegen

Beim Aufruf einer Funktion werden die Parameter von links nach rechts belegt, so wie es bei der Funktionsdefinition festgelegt wurde. Wenn Funktionen aber sehr viele Parameter haben, insbesondere, wenn sie zu einem großen Teil vorbelegt sind, kann es praktisch sein, nur einzelne Parameter zu verwenden und diese zu benennen.

Dazu wird in der Klammer des Funktionsaufruf eine Zuweisung des Wertes an den Parameternamen durchgeführt.

Schauen wir auf diese Version von zaehle:

def zaehle(start=1, ende=10, schritt=1):
   for i in range(start,ende+schritt,schritt):
       print(i)
Prinzipiell müssen nicht alle Parameter vorbelegt sein. Die nicht belegten müssen allerdings zuerst aufgezählt sein und auf jeden Fall in der richtigen Reihenfolge aufgerufen werden.

Die obige Funktion kann beispielsweise so aufgerufen werden:

>>> zaehle(ende=3)
1
2
3
>>> zaehle(1, 5, schritt=2)
1
3
5
Da der Name angesprochen wird, kann die Reihenfolge der Parameter egal sein. Durch diese Aufrufmethode wird gleichzeitig dokumentiert, welche Parameter gemeint sind. Das ist bei mehreren Parametern durchaus hilfreich. Beispiel:
termin(12,5)
Ist der Termin nun am 12.5. oder nach amerikanischer Lesart am 5.12.? Die Nennung der Parameternamen bringt Klarheit.
termin(monat=12,tag=5)
Nun ist es auch egal, ob der Programmierer der Funktion termin den tag oder den monat als ersten Parameter verwendet hat.

Variable Anzahl von Funktionsparametern

Mithilfe von Tupeln können Sie Funktionen eine variable Anzahl von Parametern verleihen. Die einfachste Version dies zu realisieren, ist dass Sie ein Tupel als Parameter verwenden. Im folgenden Beispiel ist der erste Parameter noch fest. Erst ab dem zweiten wird ein Tupel übergeben.
def calltupel(fest, tupelparameter):
    print(fest)
    for element in tupelparameter:
        print(element)

calltupel(12, (1, "Schau an", 3))
Sie müssen beim Aufruf natürlich darauf achten, dass die variablen Parameter in extra Klammern eingeschlossen sind, sonst meldet sich der Interpreter mit der Meldung, dass er deutlich weniger Parameter erwartet hatte.

Die andere Variante besteht darin, dass Sie die Tupel-Auflösung im Funktionskopf durchführen und einen Stern vor den Parameter setzen.

def tumehr(fest, *parameter):
    print(fest)  # nicht ueberraschend
    for element in parameter:
        print(element)

tumehr("Fest", 12, "locker")

Rückgabewert

Eine mathematische Funktion liefert immer einen Wert zurück. Das kann eine Funktion in Python auch. Dazu muss am Ende der Funktion lediglich ein Wert mit dem Befehl return zurückgegeben werden.
def summiere(ende):
   summe = 0
   for i in range(1,ende+1,1):
       summe += i
   return summe

Summe = summiere(10)
print(summiere(3))
Die Variable Summe wird durch den Rückgabewert der Funktion summiere() initialisiert.

Wird eine Funktion nicht mit return abgeschlossen, wird automatisch return None hinzugefügt.

Rückgabe mehrerer Werte

Eine Funktion liefert, wie oben gesagt, immer einen Wert zurück. Manchmal möchte man mehrere Werte zurückgeben. Das funktioniert bei Python deshalb recht einfach, weil nicht nur eine Variable, sondern auch ein Tupel zurückgegeben werden kann.

Stehen hinter dem Befehl return mehrere Werte, die durch Komma getrennt sind, interpretiert Python diese sofort als Tupel.

def gibMalEinDatum():
    return 28,12,1998
Nun kann man die Rückgabe in einer Variable speichern, die von Python sofort als Tupel behandelt wird, oder aber man gibt drei Variablen kommasepariert auf die linke Seite des Zuweisungszeichens.
>>> datum = gibMalEinDatum()
>>> print(datum)
(28, 12, 1998)
>>> tag, monat, jahr = gibMalEinDatum()
>>> print(monat)
12
Wies Sie sehen, können Sie die Bestandteile des Datums auch wieder zerlegen, indem Sie links neben das Gleichheitszeichen drei Variablen setzen. Je eine für den Tag, den Monat und das Jahr. Es müssen aber genau so viele Variablen sein, wie Elemente in dem Tupel existieren, sonst hustet Ihnen der Interpreter einen ValueError vor.

Doc-Strings

Um Methoden zu dokumentieren, kann man einen mehrzeiligen String mit drei Anführungszeichen einleiten. Dieser wird als Doc-String behandelt.
def doppelt(wert):
    """Die Funktion verdoppelt den übergebenen Wert und gibt ihn zurück"""
    return wert*2
In diesem Fall hat der Programmierer der Funktion doppelt in dem String eine Erläuterung beigefügt. Diese kann mit dem Aufruf von help mit dem Funktionsnamen als Parameter aufgerufen werden.
help(doppelt)
Im Interpreter erscheint eine komplette Seite mit dem Funktionsnamen und dem Parameter gefolgt von dem Doc-String. Um diese Seite wieder zu verlassen, muss man die Taste Q drücken.

Globale und lokale Variablen

Variablen, die innerhalb einer Funktion initialisiert werden, sind außerhalb der Funktion nicht zugreifbar. Darum bezeichnet man sie als lokale Variablen.

Wenn eine Funktion einer globalen Variablen einen Wert zuweisen will, dann entsteht ein Problem. Der Interpreter hält dies für das Anlegen einer lokalen Variablen. Die globale Variable bleibt unverändert.

summe = 0

def summiere(ende):
   summe = 0
   for i in range(1,ende+1,1):
       summe += i

summiere(10)
print(summe)   # Das Ergebnis ist 0!
Das ist grundsätzlich auch ganz gut so. Denn grundsätzlich sollten die Werte, die von der Funktion verändert werden, über den Befehl return zurückgegeben werden und nicht per Seiteneffekt auf globale Variablen.

Andererseits kann es Situationen geben, in denen Sie unbedingt eine Variable außerhalb der Funktion verändern müssen. In diesem Fall können Sie sich helfen, indem Sie die Variale explizit als global deklarieren.

summe = 0

def summiere(ende):
   global summe # Verwende die globale Variable!
   summe = 0
   for i in range(1,ende+1,1):
       summe += i

summiere(10)
print(summe)   # Das Ergebnis ist nun 55!
Parametervariablen sind ebenfalls lokale Variablen.

Rekursion

Rekursionen sind Funktionen, die sich selbst aufrufen. Dieses Werkzeug ist mächtig und in bestimmten Situationen unabdingbar. Eine ausführlichere Betrachtung befindet sich hier, allerdings sind dort die Beispielprogramme in Java und C gehalten.

Da die Funktion nach ihrem Ablauf immer zu dem Punkt zurückkehrt, wo sie aufgerufen wurde, ist es auch möglich, dass sich die Funktion selbst aufruft. Prinzipiell ist das mit einer Schleife vergleichbar. Und wie bei einer Schleife ist es auch bei der Rekursion wichtig, darauf zu achten, dass es eine saubere Abbruchbedingung gibt.

Als einfaches Beispiel soll die rekursive Funktion zählen, allerdings zunächst rückwärts.

def zaehleRekursiv(bis):     # rekursive Funktion
    if bis>0:                # Endebedingung der Rekursion
       print(bis)            # Aktion
       zaehleRekursiv(bis-1) # Selbstaufruf

zaehleRekursiv(3)
Die Funktion wird von außen mit dem Parameter 3 aufgerufen. Da dieser größer als 0 ist, wird er zunächst auf dem Bildschirm ausgegeben. Dann ruft sich die Funktion selbst auf und vermindert dabei den Zähler um 1. Die Funktion wird also mit 2 aufgerufen, gibt diese aus, ruft sich mit 1 auf, gibt diese aus und ruft sich schließlich mit 0 auf.

Nun aber wird die Abfrage verhindern, dass sich die Funktion selbst aufruft. Stattdessen tut sie nichts weiter und kehrt zu ihrem Selbstaufruf zurück. Dort ist die Variable bis noch 1. Da aber kein Befehl mehr folgt, läuft die Funktion wieder auf ihr Ende, springt zurück zum Selbstaufruf. Dies wiederholt sich, bis die Funktion zu ihrem ersten Aufruf zurückkehrt, der außerhalb der Funktion liegt.

Um nun von 1 bis 3 zu zählen, müssen Sie den print()-Aufruf nur hinter den Selbstaufruf stellen. Dann nämlich ruft sich die Funktion immer wieder selbst auf, bis die Variable bis 0 ist. Sie kehrt dann hinter den Selbstaufruf zurück. Dort ist bis dann 1 und wird per print() ausgegeben. Wieder läuft die Funktion auf ihr Ende, springt hinter den Selbstaufruf und gibt eine 2 aus.

def zaehleRekursiv(bis):     # rekursive Funktion
    if bis>0:                # Endebedingung der Rekursion
       zaehleRekursiv(bis-1) # Selbstaufruf
       print(bis)            # Aktion

zaehleRekursiv(3)
Sie werden zurecht darauf hinweisen, dass es viel einfacher ist, mit einer Schleife zu zählen. Dennoch sollten Sie sich die beiden Zählrekursionen genauer anschauen, bis Sie verstanden haben, warum sie so funktionieren.

Rekursionen können bestimmte Probleme und Datenstrukturen extrem gut bearbeiten. Ein typisches Beispiel ist das Durchlaufen von Baumstrukturen, wie es die Verzeichnisstruktur einer Festplatte darstellt.

Übungsaufgaben

Im Beispielprojekt Codeknacker wird gezeigt, wie man mit Funktionen einen Top-Down-Entwurf verfolgt.

Fakultät

Die Fakultät von n ist so definiert, dass alle Zahlen von 1 bis n miteinander multipliziert werden. Also ist die Fakultät von 3 das Ergebnis von 1 * 2 * 3, also 6. Die von 5 ist 1 * 2 * 3 * 4 * 5, was 120 ergibt.

Dafür kann man eine Funktion schreiben, die n als Parameter übernimmt und das Ergebnis als Rückgabewert zurückgibt.

Größter gemeinsamer Teiler

Der ggT genannte größte gemeinsame Teiler ist die Zahl, durch die zwei andere teilbar sind, um genau zu sein, die größte davon. Diese Zahl ist beispielsweise beim Kürzen von Brüchen sehr nützlich.

Ein schönes Verfahren wird bei den Übungsaufgaben für Schleifen dargestellt. Diese werden Sie vielleicht schon gelöst haben.

Nun sollen Sie eine Funktion schreiben, die die beiden Zahlen als Parameter entgegen nimmt und den ggT als Rückgabewert liefert. Der Aufruf könnte dann so lauten:

wert = ggT(42, 36)

Rekursive Fakultät

Falls Ihnen die Fakultät oben Spaß gemacht hat, können Sie diese ja einmal rekursiv ermitteln.

Video