Funktionen

Einleitung

Functionen Das Konzept einer Funktion gehört wohl zu den bedeutendsten der Mathematik. Auch in Programmiersprachen werden Funktionen häufig eingesetzt, um mathematische Funktionen in Algorithmen umzusetzen. Eine solche Funktion berechnet ein oder mehrere Werte und ist vollständig von den übergebenen Eingabewerte, den sogenannten Parametern, abhängig.

Ganz allgemein gesehen stellt eine Funktion ein Strukturierungselement in einer Programmiersprache dar, um eine Menge von Anweisungen zu gruppieren, damit man sie mehrmals im Programm verwenden kann. Ohne Funktionen könnte man Code nur kopieren, um ihn wiederzuverwenden. Dann müsste man den Code entsprechend anpassen. Spätere Änderungen müssten dann an allen kopierten Stellen nachgezogen werden, was natürlich sehr fehlerträchtig wäre. Die Benutzung von Funktionen erhöht die Verständlichkeit und Qualität eines Programmes oder Skriptes. Außerdem senkt die Verwendung von Funktionen auch die Kosten für die Softwareentwicklung und die Wartung der Software.

Funktionen kennt man unter unterschiedlichen Bezeichnungen in verschiedenen Programmiersprachen. So kennt man sie auch als Subroutinen, Routinen, Prozeduren, Methoden und Unterprogrammen.

Syntax in Python

Funktionen werden mit dem Schlüsselwort def eingeleitet
def funktions-name(Parameterliste):
    Anweisung(en)

Die Parameterliste besteht aus einem oder mehr Bezeichnern, die durch Kommata getrennt sind. Die Parameter der Definition werden beim Aufruf der Funtion als Argumente bezeichnet. Meistens werden die beiden Begriffe jedoch fälschlicherweise wie Synonyme verwendet.
Parameter können obligatorisch und optional sein. Die optionalen Parameter (0 oder mehr) folgen den obligatorischen.

Der Funktionskörper (function body), also die Anweisungen, die ausgeführt werden, wenn die Funktion aufgerufen wird, wird in Python durch eine homogene Einrückung markiert. Also ebenso wie alle anderen Blöcke in Python. Die Funktionsdefinition ist beendet, wenn eine Anweisung erfolgt, die wieder auf der selben Einrückungsstufe steht, wie der Kopf der Funktion.

Der Funktionskörper kann ein oder mehrere return-Anweisungen enthalten. Diese können sich an beliebiger Stelle innerhalb des Funktionskörpers befinden. Eine return-Anweisung beendet den Funktionsaufruf und das Ergebnis des Ausdrucks, der hinter der return-Anweisung steht, wird an die aufrufende Stelle zurückgeliefert. Falls kein Ausdruck dem return folgt, wird der spezielle Wert None zurückgeliefert. Wird das Ende eines Funktionskörpers erreicht, ohne auf eine return-Anweisung gestoßen zu sein, endet der Funktionsaufruf und es wird ebenfalls der Wert None zurückgegeben.
Beispiel:
def fahrenheit(T_in_celsius):
    """ returns the temperature in degrees Fahrenheit """
    return (T_in_celsius * 9 / 5) + 32

for t in (22.6, 25.8, 27.3, 29.8):
    print(t, ": ", fahrenheit(t))
Die Ausgabe des obigen Skriptes mit der Funktion fahrenheit:
22.6 :  72.68
25.8 :  78.44
27.3 :  81.14
29.8 :  85.64

Beispiel einer Funktion mit optionalen Parametern

Funktionen können auch optionale Parameter haben. Man nennt sie auch Default-Parameter. Dies sind Parameter, die beim Aufruf nicht angegeben werden müssen. In diesem Fall werden dann Default-Werte für diese Parameter eingesetzt.
Wir zeigen dies an einem Beispiel. Das folgende kleine Skript, das nicht sehr nützlich ist, begrüßt eine Person mit Namen. Falls beim Aufruf allerdings kein Name übergeben wird, druckt sie nur "Hello everybody!":

def Hello(name="everybody"):
    """ Greets a person """
    print("Hello " + name + "!")

Hello("Peter")
Hello()
Die Ausgabe sieht dann folgendermaßen aus:
Hello Peter!
Hello everybody!


Docstring

Die erste Anweisung eines Funktionsrumpfes ist normalerweise eine Zeichenkette, die als Funktionsname.__doc__ abfragbar ist.
Diese Anweisung wird Docstring genannt.
Beispiel:

def Hello(name="everybody"):
    """ Greets a person """
    print("Hello " + name + "!")

print("The docstring of the function Hello: " + Hello.__doc__)
Die Ausgabe:
The docstring of the function Hello:  Greets a person 


Schlüsselwortparameter

Dabei handelt es sich um eine alternative Möglichkeit eine Funktion aufzurufen. Die Funktionsdefinition selbst ändert sich nicht.
Ein Beispiel:

def sumsub(a, b, c=0, d=0):
    return a - b + c - d

print(sumsub(12,4))
print(sumsub(42,15,d=10))
Als Schlüsselwort-Parameter dürfen nur solche verwendet werden, die nicht bereits als Positionsargumente verwendet wurden. Wir können den Vorteil im Beispiel sehen. Wenn wir keine Schlüsselwortparameter hätten, müssten wir im zweiten Funktionsaufruf alle Argumente angeben, obwohl c nur den Default-Wert haben muss:
print(sumsub(42,15,0,10))

Rückgabewerte

In unseren früheren Beispielen haben wir return-Anweisungen in der Funktion sumsub aber nicht in Hello verwendet. Man sieht also, dass es nicht zwingend notwendig ist eine return-Anweisung in einer Funktion zu haben. Aber was wird zurückgeliefert, wenn wir keine explizite Rückgabeanweisung haben. Wir wollen es uns an einem Beispiel anschauen:
def no_return(x,y):
    c = x + y

res = no_return(4,5)
print(res)
Wenn wir das obige kleine Skript starten, wird None gedruckt, d.h. der spezielle Wert None wird von der Funktion no_return zurückgeliefert. Das zeigt, das eine Funktion, die ohne return Anweisung endet, None zurückliefert. Dieser Wert wird aber auch zurückgeliefert, wenn eine return-Anweisung ohne nachfolgenden Ausdruck eine Funktion beendet, wie wir im folgenden Beispiel sehen:
def empty_return(x,y):
    c = x + y
    return

res = empty_return(4,5)
print(res)
Ansonsten wird der Wert des Ausdrucks hinter dem return zurückgeliefert. Im nächsten Beispiel wird 9 zurückgeliefert:
def return_sum(x,y):
    c = x + y
    return c

res = return_sum(4,5)
print(res)

Mehrere Rückgabewerte

Eine Funktion kann genau einen Wert zurückliefern, oder wir sollten besser sagen genau ein Objekt. Ein Objekt kann beispielsweise ein numerischer Wert, wie eine ganze Zahl (Intger) oder eine Nachkommazahl (float) sein, aber auch ein komplexes Objekt wie beispielsweise eine Liste oder ein Dictionary. Wenn wir beispielsweise drei Integers zurückliefern wollen, haben wir die Möglichkeit diese in ein Tuple oder eine Liste zu packen und dieses Listen- oder Tuple-Objekt als return-Wert zu übergeben. Das bedeutet, dass wir indirekt auf eine sehr einfache Art und Weise beliebig viele Werte zurückliefern können.

Im folgenden Beispiel berechnet die Funktion fib_intervall die Fibonacci-Begrenzung für eine beliebe positive Zahl, d.h. das heißt, sie liefert ein 2-Tupel zurück. Das erste Element ist die größte Fibonacci Zahl, die kleiner oder gleich x ist und die zweite Komponente ist die kleinste Fibonacci Zahl größer oder gleich x. Der Rückabewert wird direkt mittels "unpacking" in die Variablen lub und sup gespeichert:
def fib_intervall(x):
    """ returns the largest fibonacci
    number smaller than x and the lowest
    fibonacci number higher than x"""
    if x < 0:
        return -1
    (old,new, lub) = (0,1,0)
    while True:
        if new < x:
            lub = new 
            (old,new) = (new,old+new)
        else:
            return (lub, new)
            
while True:
    x = int(input("Your number: "))
    if x <= 0:
        break
    (lub, sup) = fib_intervall(x)
    print("Largest Fibonacci Number smaller than x: " + str(lub))
    print("Smallest Fibonacci Number larger than x: " + str(sup))

Lokale und globale Variablen in Funktionen

Variablen sind standardmäßig (by default) lokal in einer Funktion, in der sie definiert werden.
def f(): 
    print(s)
s = "Python"
f()
arrow
Ausgabe:
Python
def f(): 
    s = "Perl"
    print(s)

s = "Python"
f()
print(s)
arrow
Ausgabe:
Perl
Python
def f(): 
    print(s)
    s = "Perl"
    print(s)

s = "Python"
f()
print(s)
arrow
Wenn wir das nebenstehende Skript ausführen, erhalten wir die Fehlermeldung:
UnboundLocalError: local variable 's' referenced before assignment
Die Variable s ist in f() mehrdeutig, d.h. im ersten print von f() könnte es sich um die globale Variable s mit dem Wert "Python" handeln. Anschließend definieren wir eine lokale Variable mit dem Wert "Perl"
def f():
    global s
    print(s)
    s = "dog"
    print(s)
s = "cat"
f()
print(s)
arrow
Im Skript auf der linken Seite haben wir in der Funktion f() durch die Anweisung global definiert, dass es sich bei der Variablen s der Funktion um das globale s handeln soll. Deshalb wirken sich alle Änderungen an s innerhalb der Funktion auch auf das s außen aus.

Ausgabe:
cat
dog
dog

Beliebige Anzahl von Parametern

Man hat sehr häufig Fälle, in denen die Anzahl der beim Aufruf nötigen Parameter a-priori nicht bekannt ist. Man kann dies in Python durch eine sogenannte Tupel-Refernz realisieren. Der letzte Parameter erhält einen * vorangestellt, was man keineswegs mit der entsprechenden C-Syntax verwechseln sollte.
Beispiel:

def arithmetic_mean(*args):
    sum = 0
    for x in args:
        sum += x
    return sum

print(arithmetic_mean(45,32,89,78))
print(arithmetic_mean(8989.8,78787.78,3453,78778.73))
print(arithmetic_mean(45,32))
print(arithmetic_mean(45))
print(arithmetic_mean())

Ergebnisse:
244
170009.31
77
45
0