print("Hello")
Hello
Funktionen kann man sich wie “Mini-Programme” bzw. “Mini-Scripts” vorstellen. Sie gruppieren mehrere Anweisungen zu einem zusammengehörigen Block. Eine Funktion in Python ist durchaus mit einer mathematischen Funktion vergleichbar, wie zum Beispiel der Quadratfunktion \(f(x) = x^2\). Diese mathematische Funktion \(f\) berechnet aus einem gegebenen Wert \(x\) einen neuen Wert \(x^2\). Ein weiteres Beispiel ist die Quadratwurzelfunktion \(f(x) = x^{\frac{1}{2}}\), welche die Quadratwurzel einer Zahl \(x\) berechnet.
Auch in Python hat eine Funktion eine klar definierte Aufgabe. Hier dienen Funktionen unter anderem dazu, Programmcode zu strukturieren (was die Lesbarkeit erhöht) und wiederverwendbar zu machen. Funktionen machen Programme in der Regel auch kürzer, weil wiederholt auszuführender Code in Funktionen ausgelagert werden kann. Sollten einmal Änderungen notwendig sein, muss man diese nur an einer Stelle innerhalb der Funktion vornehmen. Funktionen lassen sich außerdem auch in anderen Programmen wiederverwenden.
Eine Funktion aufrufen bedeutet, dass der in ihr enthaltene Code (also das “Mini-Programm”) ausgeführt wird – die Funktion “verrichtet” also ihre Arbeit.
In Python ruft man eine Funktion mit ihrem Namen gefolgt von einem runden Klammernpaar auf. Innerhalb der Klammern werden die Argumente für die Funktion übergeben (falls notwendig). Mit Argumenten übergibt man der Funktion Werte, welche diese für die Ausführung benötigt. Beispielsweise benötigt die Funktion type
ein Argument, denn sonst kann sie ihre Aufgabe nicht erfüllen – nämlich den Typ des übergebenen Arguments zu bestimmen. Gleichermaßen braucht auch die Funktion math.sqrt
aus dem math
-Modul ein Argument, damit diese die Wurzel des Arguments (eine Zahl) berechnen kann.
Hier sind zwei Beispiele für Aufrufe zweier bereits bekannter Funktionen mit jeweils einem Argument:
print("Hello")
Hello
type("Hello")
str
Es gibt aber auch Funktionen, die man ohne Argument aufrufen kann (der folgende Funktionsaufruf bewirkt eine Ausgabe einer Leerzeile am Bildschirm):
print()
Wie viele Argumente eine Funktion tatsächlich benötigt, hängt ganz alleine von der jeweiligen Funktion ab (dies ist in ihrer Dokumentation nachzulesen).
Lässt man die Klammern weg, wird die Funktion nicht aufgerufen – es wird dann lediglich der Wert (also das Funktionsobjekt) angezeigt, wenn man Python im interaktiven Modus verwendet.
print
<function print>
Dabei handelt es sich um einen Namen, welcher auf ein Funktionsobjekt verweist:
type(print)
builtin_function_or_method
Das Prinzip der Namen in Python haben wir bereits in der letzten Einheit kennengelernt. Es ist unerheblich, auf welches Objekt ein Name verweist, dies kann eine Ganzzahl, eine Funktion, oder jedes beliebige andere Objekt sein.
Wir sind glücklicherweise nicht auf bereits existierende Funktionen beschränkt, sondern können selbst unsere eigenen Funktionen schreiben. In Python wird eine Funktion wie folgt definiert:
def function_name(parameter1, parameter2, ...):
<do something>
...<optionally return something>
Eine Funktionsdefinition beginnt immer mit dem Schlüsselwort def
. Danach folgt der (frei wählbare) Funktionsname. Die PEP8-Konvention schreibt vor, dass Funktionsnamen aus Kleinbuchstaben getrennt mit Unterstrichen bestehen sollten, z.B.:
def test_function
Nach dem Funktionsnamen folgt ein Paar runder Klammern. Innerhalb dieser können von der Funktion benötigte Parameter aufgelistet werden (mehrere Parameter werden durch Kommas voneinander getrennt). Die Parameter werden dann mit spezifischen Werten, welche beim Aufruf übergeben werden (den sogenannten Argumenten), ersetzt. Es gibt aber auch Funktionen, die keine Parameter haben – die beiden runden Klammern müssen aber immer vorhanden sein:
def test_function() # ohne Parameter
Falls eine Funktion zwei Parameter haben soll, würde man dies wie folgt anschreiben:
def test_function(n, v) # zwei Parameter namens n und v
Unabhängig von den Parametern schließt ein Doppelpunkt den sogenannten Funktionskopf ab:
def test_function(n, v):
Nun folgt der Code, welcher von der Funktion ausgeführt wird wenn diese aufgerufen wird – dieser Code muss eingerückt sein. Man spricht hier vom sogenannten Funktionskörper. Im Funktionskörper kann man insbesondere alle Parameter wie normale Namen verwenden – diese sind nur innerhalb der Funktion vorhanden und erhalten die Werte der Argumente, welche beim Funktionsaufruf übergeben wurden.
Das folgende Beispiel definiert eine Funktion namens test_function
, welche zwei Zeilen Code im Funktionskörper aufweist:
def test_function():
= "Hello world!"
s print(s)
Man beachte, dass die Funktion hier lediglich definiert wurde, d.h. sie wurde noch nicht ausgeführt. Sie ist aber dem Python-Interpreter ab jetzt bekannt (d.h. es existiert ein Name test_function
, welcher auf ein Funktionsobjekt verweist):
test_function
<function __main__.test_function()>
type(test_function)
function
Aufgerufen kann unsere Funktion nun wie folgt werden (die runden Klammern sind essentiell):
test_function()
Hello world!
Im Fall einer Funktion mit Parametern würden die Definition und der Aufruf so aussehen:
def test_function_2(n, v): # Definition, zwei Parameter
if v:
print(n)
"Hello world!", True) # Aufruf mit konkreten Argumenten test_function_2(
Hello world!
"Hi!", False) # weiterer Aufruf mit anderen Argumenten test_function_2(
Übergibt man beim Aufruf dieser Funktion nicht genau die erwartete Anzahl an Argumenten, bekommt man einen Fehler:
# zwei Argumente erwartet, aber keine übergeben! test_function_2()
TypeError: test_function_2() missing 2 required positional arguments: 'n' and 'v'
Funktionen können Werte (Ergebnisse) zurückgeben. In beiden soeben definierten Funktionen test_function
und test_function_2
wird allerdings kein Wert explizit zurückgegeben (d.h. die Funktionen führen nur Code aus und geben None
zurück). Diese beiden Funktionen haben also keinen Wert wenn man sie aufruft, sie sind also keine Ausdrücke.
Wenn eine Funktion explizit einen Wert (ein Ergebnis) zurückgeben soll, verwendet man dazu das Schlüsselwort return
gefolgt vom gewünschten Rückgabewert:
def add_one(number):
"""Increment a given number by one."""
return number + 1
Wenn die Funktion aufgerufen wird, dann wird ihr Code ausgeführt bis die Zeile mit return
erreicht wird. Diese bewirkt, dass die Funktion sofort verlassen und der angegebene Wert zurückgegeben wird. Sollten also nach dem return
-Befehl noch weitere Zeilen Code im Funktionskörper folgen, werden diese nicht mehr ausgeführt.
5) add_one(
6
Ein Funktionsaufruf wird also auf seinen Rückgabewert reduziert. Nun kann man diesem auch einen Namen geben:
= add_one(9) x
x
10
Oder man kann den Wert einer Funktion (entspricht dem Rückgabewert) auch explizit am Bildschirm ausgeben (d.h. als Argument der print
-Funktion übergeben):
print(add_one(122))
123
Man kann überall dort, wo man einen Wert angeben kann, auch einen Ausdruck (eine beliebige Kombination aus Werten und Operatoren) einsetzen – eben auch eine Funktion, die einen Wert zurückgibt. Dies wird in der Informatik als Komposition bezeichnet. Die Werte werden der Reihe nach (von innen nach außen) verarbeitet und eingesetzt, bis zum Schluss ein einziger Wert übrig bleibt:
1))) add_one(add_one(add_one(
4
Eine Funktion kann keine Parameter haben oder eine bestimmte (bzw. auch unbestimmte) Anzahl an Parametern erwarten. Wenn eine bestimmte Anzahl an Parametern im Funktionskopf definiert wird, können einzelnen Parametern Standardwerte (sogenannte Default-Argumente) zugewiesen werden. Dies bedeutet, dass die Funktion auch mit weniger Argumenten als erwartet aufgerufen werden kann, wenn für die fehlenden Argumente Standardwerte existieren.
def add(number, increment=1): # das Default-Argument für increment ist 1
return number + increment
Die Funktion kann jetzt mit zwei Argumenten oder nur mit dem ersten Argument aufgerufen werden:
7, 1) add(
8
7) # hier wird das Default-Argument verwendet add(
8
7, 3) add(
10
Funktionen können auch so aufgerufen werden, dass die Namen der Parameter gemeinsam mit den Argumenten in der Form kwarg=value
explizit hingeschrieben werden. Man spricht dann von Keyword-Argumenten (nicht zu verwechseln mit den Python-Schlüsselwörtern). D.h. die obige Funktion add
kann auch so aufgerufen werden:
=5) add(number
6
=5, increment=2) add(number
7
=2, number=5) add(increment
7
Dies dient einerseits der besseren Lesbarkeit, da unmittelbar klar ist, welche Parameter welche konkreten Argumente erhalten. Andererseits kann man so die Argumente auch in beliebiger Reihenfolge übergeben.
Wenn der Argumentname nicht angegeben wird, wird die Position des Arguments bei der Zuweisung herangezogen. Man spricht in diesem Fall von positionalen Argumenten. Man kann positionale und Keyword-Argumente beim Funktionsaufruf auch mischen, aber alle positionalen Argumente müssen vor dem ersten Keyword-Argument kommen.
5, increment=2) add(
7
Zur Veranschaulichung dient folgende etwas komplexere Funktionsdefinition:
def test(name, number, exponent=5, skip=7, text="Hello"):
print(text, name, end=" ")
return number**exponent - skip
Die folgenden drei Funktionsaufrufe verwenden nur positionale Argumente:
"Python", 2) test(
Hello Python
25
"Python", 3) test(
Hello Python
236
"Test", 3, 4) test(
Hello Test
74
Keyword-Argumente sind sehr praktisch, wenn man für die meisten Parameter deren Default-Argumente verwenden möchte, aber z.B. für einen einzigen Parameter einen anderen Wert setzen will. Dann muss man nämlich nicht alle Argumente der Reihe nach übergeben, sondern nur jene, für die man andere Werte als die Standardwerte haben möchte:
"Test", 2, skip=2) test(
Hello Test
30
Ohne Keyword-Argumente würde derselbe Aufruf nämlich wie folgt aussehen:
"Test", 2, 5, 2) test(
Hello Test
30
Man muss also in diesem Beispiel ein zusätzliches Argument 5
(für den Parameter exponent
) übergeben, wenn man positionale Argumente verwendet.
Alles, was innerhalb einer Funktion definiert wird, ist nur innerhalb dieser Funktion sichtbar und zugreifbar. Man spricht von einem lokalen Scope, welcher sich auf die Funktion und weitere untergeordnete Bereiche erstreckt. Gültigkeitsbereiche entsprechen in Python im Prinzip den Einrückungen.
def test():
= 15 # s ist nur lokal in der Funktion definiert
s print(s)
test()
15
print(s) # außerhalb der Funktion existiert s nicht
NameError: name 's' is not defined
Umgekehrt kann man aber sehr wohl auf Namen, die außerhalb eines Gültigkeitsbereichs definiert wurden, zugreifen:
= 15 # globales s
s
def test():
print(s) # s aus dem globalen Scope ist zugänglich
test()print(s) # s existiert global
15
15
Wenn man innerhalb einer Funktion einen neuen Namen erstellt, welcher auch außerhalb existiert, dann ist nur der neue lokale Name zugänglich (und der Name von außerhalb wird innerhalb der Funktion versteckt). Im folgenden Beispiel verweisen die beiden Namen s
auf unterschiedliche Objekte:
= 15 # globales s
s
def test():
= 12 # lokales s ändert globales s nicht, versteckt es aber in der Funktion
s print(s)
test()print(s)
12
15
Besonders seltsam mutet folgendes Beispiel an, welches eine Fehlermeldung verursacht:
= 15 # globales s
s
def test():
print(s) # lokales s existiert noch nicht - daher Fehler!
= 12 # lokales s
s print(s)
test()print(s)
UnboundLocalError: local variable 's' referenced before assignment
Die Zeile print(s)
in der Funktion verursacht einen Fehler, weil Python aufgrund der folgenden Zeile bereits weiß, dass s
ein lokaler Name sein wird. Daher kann print(s)
auch nicht funktionieren, weil s
in der ersten Zeile noch nicht existiert! Hier mischt Python also nicht zwischen globalen und lokalen Namen.
Möchte man aber tatsächlich den global definierten Namen verwenden, kann man dies mit dem Schlüsselwort global
tun:
= 15 # globales s
s
def test():
global s # Ermöglicht Zugriff auf das globale s
print(s)
= 12 # ändert globales s
s print(s)
test()print(s)
15
12
12
Prinzipiell sollte man die Deklaration global
in lokalen Scopes aber vermeiden und globale/lokale sauber voneinander Scopes trennen. Will man auf ein Objekt aus einem äußeren Scope zugreifen, definiert man einen Parameter damit man das gewünschte Objekt als Argument der Funktion übergeben kann:
= 15 # globales s
s
def test(s):
print(s) # lokales s (Argument)
= 12
s print(s)
print(s)
test(s)print(s)
15
15
12
15
Will man eine lokale Variable in einem äußeren Scope weiter nutzen, gibt man den entsprechenden Wert am besten mit return
zurück:
= 15 # globales s
s
def test(s):
print(s) # lokales s (Argument)
= 12
s print(s)
return s
print(s)
= test(s) # globales s bekommt Rückgabewert der Funktion (lokales s)
s print(s)
15
15
12
12
Suchen Sie sich aus der Liste der eingebauten Funktionen (siehe vorige Einheit) drei beliebige Funktionen aus.
Schreiben Sie eine Funktion mult
, welche zwei Zahlen multipliziert und deren Produkt zurückgibt. Die beiden Zahlen sollen als Argumente übergeben werden können. Rufen Sie Ihre Funktion mit ein paar Wertepaaren auf und stellen Sie sicher, dass Ihre Funktion wie gewünscht funktioniert. Die Funktion soll das Ergebnis als Wert zurückgeben, d.h. verwenden Sie innerhalb der Funktion kein print
sondern return
!
Schreiben Sie eine Funktion to_fahrenheit
, welche einen Parameter celsius
hat und diese Celsius-Temperatur in Fahrenheit umwandelt und zurückgibt. Zum Testen rufen Sie Ihre Funktion mit den Celsius-Temperaturen 0, 20, 38 und 100 auf.
Schreiben Sie außerdem eine Funktion to_celsius
(mit entsprechendem Parameter fahrenheit
) und wandeln Sie die vier Fahrenheit-Temperaturen, die Sie vorher beim Aufruf der Funktion to_fahrenheit
erhalten haben, wieder in Celsius-Temperaturen um!
Nennen Sie mindestens drei Gründe, warum man Funktionen verwendet. Erklären Sie auch kurz den Unterschied zwischen Funktionsdefinition und Funktionsaufruf! Erklären Sie den Unterschied zwischen Parameter und Argument!
Schreiben Sie eine Funktion namens nonsense
, welche drei Parameter namens a
, b
und c
hat. Die beiden Argumente b
und c
sollen optional sein und die Standardwerte 10 bzw. 13 besitzen. Die Funktion soll a**2 - b * 2 + c**2
berechnen und zurückgeben (wir setzen voraus, dass man für a
, b
und c
immer Zahlenwerte übergibt). Rufen Sie die Funktion dann mit folgenden Argumenten auf:
Wie sehen diese Funktionsaufrufe aus? Wie lauten die Rückgabewerte? Geben Sie für jeden Aufruf die Werte aller drei Argumente an!
Anmerkung: Nur der erste Funktionsaufruf (ohne Argumente) verursacht einen Fehler, alle weiteren Aufrufe sind möglich!