Vorlesungsskript 18
Funktionen
Wir haben schon einige Funktionen kennengelernt, die in Python eingebaut
sind (z.B. len
, max
, input
) oder von Modulen zur
Verfügung gestellt werden (z.B. fileinput.input
). Funktionen sind
sozusagen „Programme innerhalb von Programmen“: Man ruft sie
mit einem Input (den Argumenten) auf, sie tun etwas und geben
einen Output (den Rückgabewert) zurück.
Sie können nicht nur bestehende Funktionen verwenden. Sie können auch Ihre eigenen Funktionen definieren. Auf diese Weise können Sie Ihr Programm in mehrere „Unterprogramme“ aufteilen, also Funktionen, jede mit ihrer eigenen und relativ eng begrenzten Aufgabe. Dieses Aufteilen von Programmen hat vor allem zwei Vorteile:
- Übersichtlichkeit. Programme, die in mehrere kleinere Einheiten (Funktionen) aufgeteilt sind, sind übersichtlicher. Fehler lassen sich leichter finden und beheben: Man sorgt zunächst dafür, dass eine Funktion funktioniert wie gewünscht. Dann wendet man sich der nächsten zu.
- Wiederverwendbarkeit. Viele Routineaufgaben begegnen Ihnen beim Programmieren immer wieder und sind
mehreren Programmen gemeinsam oder müssen sogar an verschiedenen Stellen innerhalb
desselben Programms ausgeführt werden. Zum Beispiel: eine Datei einlesen. Oder:
einen String in Wörter zerlegen. Oder: aus einer Liste von Wörtern die Stopwörter
herausfiltern. (Denken Sie z.B. an
tokens.py
undfreq.py
.) Es empfiehlt sich, für jede dieser Aufgaben eine eigene Funktion zu schreiben. Dann kann diese Funktion von verschiedenen Stellen im Programm aus aufgerufen werden. Und sie kann sogar unverändert durch mehrere Programme verwendet werden.
In diesem Vorlesungsskript lernen Sie, wie man Funktionen definiert und sie benutzt, um Programme in übersichtliche, wiederverwendbare Einheiten aufzuteilen.
Funktionen definieren
Funktionen definiert man in Python mit Hilfe der Schlüsselwörter def
und return
.
Funktionsdefinitionen sind wie Verzweigungen und Schleifen Blockanweisungen mit
einem Kopf und einem Körper. Hier ist ein einfaches Beispiel für eine Funktion.
Sie dient dazu, den Flächeninhalt eines Dreiecks zu berechnen.
def triangle_area(base, height):
'''Calculates the area of a triangle.
Given a base of the triangle and the corresponding height, returns its
area.'''
area = base * height / 2
return area
Einmal definiert, kann man die Funktion mit Hilfe von Funktionsaufrufen benutzen. Im folgenden Beispiel berechnen wir zum Beispiel den Flächeninhalt eines Dreiecks mit Basis 2.5 und Höhe 1.2:
>>> triangle_area(2.5, 1.2)
1.5
Der Kopf einer Funktionsdefinition besteht aus dem Schlüsselwort def
(für define), dem Namen der zu definierenden Funktion, einer Parameterliste in
runden Klammern und einem Doppelpunkt.
Die Parameterliste bestimmt, mit wie vielen Argumenten die Funktion aufgerufen werden muss.
Unsere Beispielfunktion hat zwei Parameter – base
und
height
– also muss die Funktion mit genau zwei Argumenten aufgerufen werden.
Aufrufe wie triangle_area()
, triangle_area(2.5)
or
triangle_area(2.5, 1.2, 0)
würden zum Abbruch des Programms mit einer
Fehlermeldung führen.
Der Körper einer Funktionsdefinition besteht aus einem (optionalen, aber
empfohlenen) Docstring und einer Reihe von Anweisungen, darunter
auch return
-Anweisungen.
Der Docstring ist das zur Funktion gehörende „Handbuch“: Er beschreibt, was für Argumente die Funktion erwartet und was sie zurückgibt. Hier können Sie und andere Programmiererinnen nachschauen, wie Sie sie benutzen müssen. Der Docstring wird normalerweise als ein mehrzeiliges, durch dreifache Anführungszeichen begrenztes String-Literal gegeben, darin auf der ersten Zeile eine kurze Zusammenfassung, dann eine Leerzeile und schließlich eine detailiertere Beschreibung der Argumente und des Rückgabewerts.
Beim Aufruf einer Funktion – z.B. triangle_area(2.5,
1.2)
– passiert Folgendes: Die Argumente werden automatisch in
den entsprechenden Parametern gespeichert (Parameter sind eine Art Variablen),
also z.B. 2.5
in base
und 1.2
in height
. Dann werden die Anweisungen
im Körper der Funktion ausgeführt, in unserem Beispiel
zunächst die Zuweisung area = base *
height / 2
und dann die return
-Anweisung return area
. return
-Anweisungen beenden
das Ausführen der Funktion und legen gleichzeitig den
Rückgabewert des Funktionsaufrufs fest. So gibt unser
Aufruf triangle_area(2.5,
1.2)
den Wert zurück, den die Funktion
zuvor in der Variablen area
gespeichert hat (1.5
).
Endet die Ausführung einer Funktion ohne return
-Anweisung,
gibt der Funktionsaufruf None
zurück.
Lokale Variablen
Funktionen sind vom Rest des Programms unabhängige Einheiten. Das heißt
auch, dass man nicht außerhalb einer Funktion auf Variablen zugreifen kann, die
innerhalb der Funktion definiert werden: diese Variablen sind lokal,
also nur innerhalb der Funktion gültig. Auch die Parameter sind lokale
Variablen. Wenn wir nach unserem triangle_area
-Aufruf
versuchen, auf die lokalen Variablen zuzugreifen, gibt es daher
Fehlermeldungen:
>>> triangle_area(2.5, 1.2)
1.5
>>> base
Traceback (most recent call last):
File "", line 1, in
NameError: name 'base' is not defined
>>> height
Traceback (most recent call last):
File "", line 1, in
NameError: name 'height' is not defined
>>> area
Traceback (most recent call last):
File "", line 1, in
NameError: name 'area' is not defined
Umgekehrt kann eine Funktion auf Variablen, die außerhalb der Funktion definiert sind (globale Variablen), zwar lesend, aber normalerweise nicht schreibend zugreifen, wie folgende Session demonstriert:
>>> x = 5
>>> def test1():
... '''Prints the value of the global variable x.'''
... print(x)
...
>>> test1()
5
>>> def test2():
... '''Tries to set the value of the global variable x to 7.'''
... x = 7
...
>>> test2()
>>> x
5
Im obigen Beispiel definieren wir im interaktiven Modus
Funktionen, müssen also im interaktiven Modus mehrzeilige Blockanweisungen
eingeben. Das von >>>
zu ...
veränderte Python-Prompt zeigt an, dass wir noch in einer Blockanweisung stecken und
die Eingabe noch nicht beendet ist. Man beendet die Eingabe mit einer Leerzeile.
Schreibend zugreifen kann eine Funktion auf globale Variablen, wenn sie sie mit einer
global
-Anweisung als global deklariert:
>>> def test3():
... '''Sets the value of the global variable x to 7.'''
... global x
... x = 7
...
>>> test3()
>>> x
7
Die Möglichkeit, in einer Funktion lesend oder sogar schreibend auf eine globale Variable zuzugreifen, kann zwar manchmal nützlich sein. Es kann jedoch auch verwirrend sein und ist in den meisten Fällen nicht nötig. Deswegen sollte man globale und lokale Variablen normalerweise strikt trennen. Die Funktion und der Rest des Programms tauschen dann Daten ausschließlich über die Objekte aus, die als Argumente übergeben werden bzw. zurückgegeben werden.
Funktionen testen
Ein einfacher Weg, Funktionen zu testen, ist es, ein Programm zu schreiben,
das die Funktion enthält sowie eine oder mehrere Anweisungen, die die Funktion
aufrufen. Zum Beispiel können wir ein Programm triangle.py
schreiben mit folgendem Inhalt:
def triangle_area(base, height):
'''Calculates the area of a triangle.
Given a base of the triangle and the corresponding height, returns its
area.'''
area = base * height / 2
return area
print(triangle_area(2.5, 1.2))
Dieses Programm lässt sich dann wie folgt ausführen:
$ python3 triangle.py
1.5
Weitere Beispiele für Funktionen
def read_file_into_list(path):
'''Returns the lines in the given file as a list.
Trailing whitespace is removed from each line.'''
lines = []
infile = open(path, 'r')
for line in infile:
lines.append(line.rstrip())
infile.close()
return lines
def remove_stopwords(words, stopwords):
'''Removes stopwords from a sequence of words.
Given an iterable of words and a collection of stopwords, returns a new
list containing only those words from the iterable that are not
stopwords.'''
filtered = []
for word in words:
if word not in stopwords:
filtered.append(word)
return filtered
def extract_words(string):
'''Extracts the words from a string.
Returns a list of the word occurrences in string. Every contiguous sequence
of alphanumeric characters is considered a word.'''
words = []
filtered_string = ''
for character in string:
if character.isalnum():
filtered_string += character
else:
filtered_string += ' '
return filtered_string.split()
Feinheiten
Funktionsaufrufe mit Keyword-Argumenten
Funktionsaufrufe können Argumente auch als Keyword-Argumente übergeben, also explizit dazusagen, welchen Parameter das jeweilige Argument füllen soll. Keyword-Argumente müssen nach den positionsabhängigen Argument kommen. Ihre Reihenfolge untereinander ist aber egal, weil Python ja durch die Keywords weiß, für welche Parameter sie bestimmt sind.
Zum Beispiel können wir unsere Funktion triangle_area
auch wie folgt aufrufen. Wir übergeben hier das zweite Argument als Keyword-Argument:
>>> triangle_area(2.5, height=1.2)
1.5
Wir können natürlich auch beide Argumente als Keyword-Argumente übergeben:
>>> triangle_area(base=2.5, height=1.2)
1.5
In diesem Fall spielt die Reihenfolge keine Rolle mehr:
>>> triangle_area(height=1.2, base=2.5)
1.5
Funktionsdefinitionen mit Default-Werten für Parameter
Normalerweise müssen Funktionsaufrufe alle Argumente angeben. Argumente für Parameter, für die in der Parameterliste ein Default-Wert angegeben ist, sind jedoch optional. Werden sie nicht übergeben, wird der Default-Wert verwendet.
def greet(name, greeting='Hello', punctuation='!'):
print('{}, {}{}'.format(greeting, name, punctuation))
>>> greet('Harry')
Hello, Harry!
>>> greet('Harry', 'Good day')
Good day, Harry!
>>> greet('Harry', 'Good day', '.')
Good day, Harry.
Auch hier können wir Argumente statt positionsbasiert als Keyword-Argumente übergeben.
Dann können wir auch Argumente überspringen, also z.B. punctuation
angeben, aber greeting
nicht:
>>> greet('Harry', punctuation='.')
Hello, Harry.
Funktionsdefinitionen mit variadischen Parametern
Normalerweise müssen alle Argumente, die bei einem Funktionsaufruf übergeben werden, entsprechende Parameter haben, sonst kommt es zu einer Fehlermeldung:
>>> greet('Harry', 'Good day', '.', 'Voldemort', 'Hagrid')
Traceback (most recent call last):
File "", line 1, in
TypeError: greet() takes from 1 to 3 positional arguments but 5 were given
>>> greet('Harry', 'Good day', '.', 'Voldemort', 'Hagrid', location='Hogwarts', time=6)
ceback (most recent call last):
File "", line 1, in
TypeError: greet() got an unexpected keyword argument 'location'
Man kann in einer Funktionsdefinition jedoch auch den Aufruf mit beliebig
vielen weiteren positionsabhängigen und/oder Keyword-Argumenten erlauben, indem
man in die Parameterliste die variadischen Parameter *args
und/oder **kwargs
aufnimmt.
Zusätzliche positionsabhängige Argumente werden dann in der Variablen
args
gespeichert, zusätzliche Keyword-Argumente als
Dictionary in kwargs
.
def greet(name, greeting='Hello', punctuation='!', *args, **kwargs):
print('{}, {}{}'.format(greeting, name, punctuation))
print('Additional positional arguments: {}'.format(args))
print('Additional keyword arguments: {}'.format(kwargs))
>>> greet('Harry', 'Good day', '.', 'Voldemort', 'Hagrid', location='Hogwarts', time=6)
Hello, Harry.
Additional positional arguments: ('Voldemort', 'Hagrid')
Additional keyword arguments: {'location': 'Hogwarts', 'time': 6}
So ist es möglich, Funktionen zu definieren, die wie print
,
min
, max
etc. beliebig viele Argumente
akzeptieren.
Funktionsaufrufe mit variadischen Argumenten
Auch beim Aufruf von Funktionen ist es möglich, statt einzelner Argumente
eine Liste mit weiteren positionsabhängigen Argumenten und/oder ein Dictionary
mit weiteren Keyword-Argumenten zu übergeben. Hierfür setzen wir *
bzw. **
davor. Unser obiger
Aufruf triangle_area
kann somit u.a. auch auf diese
Weisen erfolgen:
>>> args = [2.5, 1.2]
>>> triangle_area(*args)
1.5
>>> kwargs = {'base': 2.5, 'height': 1.2}
>>> triangle_area(**kwargs)
1.5
>>> args = [2.5]
>>> kwargs = {'height': 1.2}
>>> triangle_area(*args, **kwargs)
1.5
Hier variadische Argumente zu verwenden dient natürlich keinem Zweck außer
der Illustration. Sie brauchen sie hingegen zum Beispiel, wenn in Ihrem Programm eine Liste
existiert und Sie dieser einer Funktion übergeben wollen, die die Elemente als einzelne
Argumente erwartet, wie z.B. die print
-Funktion:
>>> tokens = ['dog', 'cat', 'mouse']
>>> print(*tokens)
dog cat mouse