Funktionen¶
Oftmals werden bestimmte Code-Stücke an verschiedenen Stellen eines Programms eingesetzt. Derartige Teile, die gewissermaßen kleine „Unterprogramme“ darstellen, werden als Funktionen definiert. Funktionen liefern am Ende stets genau ein Ergebnis zurück; dieses kann jedoch von den übergebenen Funktionsargumenten und/oder von äußeren Bedingungen abhängig sein. [1]
Definition eigener Funktionen¶
Eine Funktion wird in Python nach folgendem Schema definiert:
def function_name(argument_1, argument_2, ...):
"""
Docstring (short description of the function's purpose)
"""
function_code
...
return result
Zum Benennen von Funktionen werden in Python üblicherweise nur kleine Buchstaben
verwendet. Häufig besteht ein Funktionsname aus einem Verb, das die
Wirkungsweise der Funktion beschreibt; liegt jedoch der Zweck einer Funktion nur
darin, einen bestimmten Wert auszugeben, so kann der Funktionsname auch aus
einem Substantiv bestehen, das den jeweiligen Wert beschreibt – beispielsweise
wird die Wurzelfunktion meist mit sqrt()
(„square root“) anstelle von
calculate_sqrt()
bezeichnet. Besteht ein Funktionsname aus mehreren Wörtern,
so werden diese zur besseren Lesbarkeit mittels Unterstrichen getrennt.
Nach ihrer Definition kann die Funktion dann folgendermaßen aufgerufen werden:
function_name(argument_1, argument_2, ...)
Eine Funktion kann, abhängig von ihrer Definition, beim Aufruf kein, ein oder
auch mehrere Argumente in der jeweiligen Reihenfolge verlangen. Bei jedem Aufruf
wird dann der Funktions-Code ausgeführt und ein entsprechender Ergebniswert
zurück gegeben; ist im Funktionsblock keine return
-Anweisung enthalten, wird
von der Funktion automatisch der Wert None
zurückgegeben. [2]
Beim Aufruf der Funktion werden die einzelnen Argumente üblicherweise gemäß
ihrer Position direkt übergeben, beispielsweise argument_1 = 5 ;
function_name(argument_1)
. Es ist jedoch auch für eine gegebenenfalls bessere
Lesbarkeit des Quellcodes möglich, bei der Übergabe von Argumenten deren
Bezeichnung mit anzugeben, beispielsweise function_name(argument_1=5)
.
Da jede Funktion ein kleines Unterprogramm darstellt, sollte sie durch einen Docstring dokumentiert werden. Es gibt Werkzeuge, die Docstrings verwenden, um automatisch gedruckte Dokumentation zu erzeugen (beispielsweise Sphinx) oder um Benutzer interaktive Hilfe anzubieten (beispielsweise Ipython). Werden bei einer Funktion Argumente verlangt, so sollten diese ebenfalls im Docstring kurz beschrieben werden (Typ, Bedeutung).
Optionale Argumente¶
In Python ist es möglich, bei der Definition von Funktionen optionale Argumente mit einem Standard-Wert anzugeben. Wird ein solches optionales Argument beim Aufruf der Funktion nicht explizit angegeben, so wird stattdessen der Standard-Wert genutzt.
Die Definition einer solchen Funktion lautet etwa:
def my_function(argument_1, argument_2="Test")
return (argument_1, argument_2)
Die Funktion kann anschließend wahlweise mit einem oder mit zwei Argumenten aufgerufen werden:
my_function(5)
# Ergebnis: (5, 'Test')
my_function(5, 7)
# Ergebnis: (5, 7)
Hat eine Funktion sowohl „normale“ als auch optionale Argumente, so müssen die optionalen Argumente am Ende der Funktion angegeben werden.
Ebenso ist es möglich, einer Funktion eine optionale Liste oder ein optionales Dict für zusätzliche Argumente anzugeben. Üblicherweise lautet die Syntax dafür:
def any_function(argument_1, argument_2="default value", *args, **kwargs)
pass
Beim Aufruf der Funktion muss argument_1
angegeben werden, die Angabe von
argument_2
ist optional; zusätzlich können weitere unbenannte oder benannte
Argumente angegeben werden, beispielsweise any_function(5,2,7,foo=9)
. Im
obigen Beispiel können alle unbenannten Argumente innerhalb der Funktion über
die Variable args
, alle benannten über die Variable kwargs
aufgerufen
werden. Werden beim Funktionsaufruf keine weiteren Argumente übergeben, so ist
args
innerhalb der Funktion ein leeres Tupel und kwargs
ein leeres Dict.
def sum_it_up(num_1, num_2, *nums):
list_sum = functools.reduce(lambda x,y: x+y, nums)
return num_1 + num_2 + len(nums)
Hat man im Quellcode vorab eine Liste an Objekten definiert, die man dann als
Argumente an eine Funktion übergeben möchte, so muss diese bei der Übergabe
entpackt werden. Dies ist mittels des *
-Operators möglich:
my_list = [1,3,5,7]
sum_it_up(*my_list)
# Ergebnis: 6
Im obigen Beispiel wurde die Liste my_list
bei der Übergabe an die Funktion
entpackt; der Aufruf von sum_it_up(*my_list)
ist in diesem Fall also
identisch mit sum_it_up(1,3,5,7)
. Die ersten beiden Zahlen werden dabei als
Argumente für num_1
und num_2
angesehen, die verbleibenden beiden
werden innerhalb der Funktion in die Variable nums
gespeichert. [3]
Nach dem gleichen Prinzip kann ein Dict mit Schlüsselwort-Wert-Paaren mittels
**
entpackt an eine Funktion übergeben werden; dabei müssen alle im Dict
enthaltenen Schlüsselwörter als mögliche Funktionsargumente erlaubt sein.
Veränderliche und unveränderliche Argumente¶
In Python werden Argumente an Funktionen via Zuweisung (Position oder Benennung)
übergeben. Wird beispielsweise eine Variable x = any_object
als Argument an
eine Funktion übergeben, so wird x
, also eine Referenz auf any_object
,
als Wert übergeben. Welchen Einfluss die Funktion auf das Argument hat, hängt
dabei vom Objekt-Typ ab:
Ist
any_object
ein veränderliches Objekt, beispielsweise eine Liste, ein Dict oder ein Set, so kann dieses durch die Funktion direkt verändert werden:my_list = [1,2,3] def foo(any_list): any_list.append(4) foo(my_list) print(my_list) # Ergebnis: [1,2,3,4]
Wird allerdings der übergebenen Referenz
x
in der Funktion ein neues Objekt zugewiesen, so entspricht dies einer Definition einer neuen Variablen innerhalb der Funktion als Namensraum. [4] Die ursprüngliche Referenzx
bleibt in diesem Fall unverändert und zeigt nach wie vor auf das ursprüngliche Objekt:my_list = [1,2,3] def foo(any_list): any_list = [1,2,3,4] foo(my_list) print(my_list) # Ergebnis: [1,2,3]
Ist
any_object
ein unveränderliches Objekt, beispielsweise ein String oder ein Tupel, so wird bei der Übergabe an die Funktion eine Kopie des Objekts erzeugt; das ursprüngliche Objekt kann somit durch die Funktion nicht verändert werden:my_string = 'Hello World!' def foo(any_string): any_string.replace('World', 'Python') foo(my_string) print(my_string) # Ergebnis: 'Hello World!'
Möchte man den obigen Beispielcode so umschreiben, dass nach Aufruf der Funktion die Variable
my_string
den Wert'Hello Python!'
bekommt, so muss man folgendermaßen vorgehen:my_string = 'Hello World!' def foo(any_string): x = any_string.replace('World', 'Python') return x my_string = foo(my_string) print(my_string) # Ergebnis: 'Hello Python!'
Da Strings nicht verändert werden können, kann eine Veränderung der zugehörigen Variablen also nur über eine neue Zuweisung erfolgen.
Die Übergabe eines veränderlichen Datentyps, beispielsweise einer Liste, als Argument an eine Funktion ist zwar performance-technisch günstig, da nur eine neue Referenz auf die Liste erzeugt werden muss; es können aber unerwartete Fehler auftreten, wenn durch eine Funktion das übergebene Original der Liste unmittelbar verändert wird. Durch Verwendung von Tupeln anstelle von Listen kann dies auf Kosten von CPU und Memory ausgeschlossen werden.
Globaler und lokaler Namensraum
Jeder Funktionsblock hat einen eigenen Namensraum, was bedeutet, dass Variablen, die innerhalb des Funktionsblocks definiert werden, nur im Programm existieren, bis der Funktionsblock abgeschlossen ist. Derartige Variablen, die nur innerhalb der Funktion „sichtbar“ sind, werden „lokale“ Variablen genannt.
Einer lokalen Variablen kann dabei der gleiche Name zugewiesen werden wie einer Variablen, die außerhalb des Funktionsblocks definiert wurden; sie „überdeckt“ in diesem Fall die nicht-lokale Variable: Der Interpreter sucht primär im lokalen Namensraum nach dem Variablennamen und erst sekundär im nicht-lokalen Namensraum, wenn keine lokale Variable mit dem angegebenen Namen existiert. und nur.
Wird einer lokalen Variablen ein Wert zugewiesen, so ändert sich also der Wert einer nichtlokalen Variablen nicht:
# Nicht-lokale Variable definieren:
x = 0
# Funktion mit lokaler Variablen definieren:
def myfunc():
x = 1
print(x)
# Funktion aufrufen:
myfunc()
# Ergebnis: 1
# Wert von x prüfen:
x
# Ergebnis: 0
Soll der Wert einer nicht-lokalen Variablen durch die Funktion verändert werden, so kann diese bei einem veränderlichen Datentyp als Argument an die Funktion übergeben werden. Variablen mit nicht veränderlichem Datentyp können, wie im letzten Abschnitt beschrieben, eine Veränderung erreicht werden, nur durch eine Zuweisung des Rückgabewerts der Funktion geändert werden.
Eine weitere prinzipielle Möglichkeit, die jedoch möglichst vermieden werden
sollte, ist es, mittels des Schlüsselworts global
einen Variablennamen
primär im nicht-lokalen Namensraum zu suchen:
# Nicht-lokale Variable definieren:
x = 0
# Funktion mit globaler Variablen definieren:
def myfunc():
global x = 1
print(x)
# Funktion aufrufen:
myfunc()
# Ergebnis: 1
# Wert von x prüfen:
x
# Ergebnis: 1
Das Schlüsselwort global
hat nur Auswirkungen innerhalb der Funktion, eine
Variable kann also nicht von außerhalb der Funktion als „global“ gekennzeichnet
und als solche der Funktion aufgezwungen werden. Dennoch kann die Verwendung von
global
zu unerwarteten Seiteneffekten führen, wenn eine Variable prinzipiell
von mehreren Stellen aus verändert werden kann, da in diesem Fall nicht immer
auf den ersten Blick einwandfrei feststellbar ist, von wo aus die Variable
verändert wurde.
Lambda-Ausdrücke¶
Bei so genannten Lambda-Ausdrücken handelt es sich um Mini-Funktionen, die sehr
kompakt implementiert werden können. Das Schlüsselwort zur Definition eines
Lambda-Ausdrucks ist lambda
, gefolgt von möglichen Argumenten, die der
Funktion beim Aufruf übergeben werden sollen, und einem Doppelpunkt.
Hinter diesem Doppelpunkt wird in der gleichen Zeile die Wirkungsweise des
Lambda-Ausdrucks definiert, beispielsweise:
add = lambda x1, x2: x1 + x2
add(5,3)
# Ergebnis: 8
Bei der Definition eines Lambda-Ausdrucks entfällt also das Schlüsselwort
def
, und die Argumente werden unmittelbar hinter dem Schlüsselwort
lambda
ohne runde Klammern angegeben.
Ein Nachteil von Lambda-Ausdrücken ist, dass in ihnen keine Schleifen, Kontrollstrukturen oder komplexeren Anweisungsblöcke vorkommen dürfen. [5] Ein wichtiger Vorteil ist hingegen, dass Lambda-Ausdrücke keinen expliziten Namen haben müssen. Beispielsweise kann der gesamte Lambda-Ausdruck, wenn man ihn in runde Klammern setzt, unmittelbar wie eine Funktion aufgerufen werden:
result = (lambda x1, x2: x_1 + x_2)(5,3)
print(result)
# Ergebnis: 8
Lambda-Ausdrücke werden auch häufig in Kombination mit den Builtin-Funktion filter() und map() eingesetzt, um jeweils auf alle Elemente einer Liste angewendet zu werden:
# Beispiel 1:
my_list = [1,2,3,4,5,6,7,8,9]
even_numbers = filter(lambda x: x % 2 == 0, my_list)
list(even_numbers)
# Ergebnis: [2,4,6,8]
# Beispiel 2:
my_list = [1,2,3,4]
even_numbers = map(lambda x: x * 2, [1,2,3,4])
list(even_numbers)
# Ergebnis: [2,4,6,8]
Ebenso kann ein Lambda-Ausdruck als Kriterium für die sort()
-Funktion
genutzt werden, um beispielsweise eine Liste von Zweier-Tupeln nach den ersten
Elementen der Tupel zu sortieren:
my_list = [(3, 1), (1, 2), (11, -3), (5, 10)]
my_list.sort(key=lambda x: x[0])
print(my_list)
# Ergebnis: [(1, 2), (3, 1), (5, 10), (11, -3)]
Auch bei der Entwicklung von graphischen Oberflächen sind Lambda-Ausdrücke nützlich, um einzelnen Maus-Events oder Tastenkombinationen bestimmte Funktionen zuzuweisen.
Builtin-Funktionen¶
Neben der bereits erwähnten print()
-Funktion, die zur Anzeige von Texten und
Variablen auf dem Bildschirm dient, gibt es in Python weitere grundsätzlich
verfügbare Funktionen, so genannte „Builtin“-Funktionen; Diese Funktionen sind
ohne das Importieren weiterer Module unmittelbar verfügbar. Eine
Übersicht über diese Funktionen findet sich im Anhang: Standard-Funktionen.
Anmerkungen:
[1] | Soll eine Funktion mehrere Ergebnisse liefern, so müssen diese als Liste oder Tupel zurückgegeben werden. |
[2] | In einer Funktionsdefinition können auch, wenn Soll eine Funktion mehrere Werte als Ergebnis liefern, so müssen diese als Liste oder Tupel an das Programm zurückgegeben werden. |
[3] | Dass die obige Funktion die Länge der zusätzlichen Zahlenliste zum
Ergebnis dazu addiert, soll an dieser Stelle nur die Funktionalität der
optionalen Argumente zeigen. Um alle Elemente dieser Liste zu summieren,
muss zusätzlich das Modul functools geladen werden; in der
Beispielaufgabe Quader wird dies näher behandelt. |
[4] | Variablen sind nur innerhalb ihres Namensraums gültig. Ausnahmen sind
globale Variablen, die beispielsweise zu Beginn eines Moduls definiert sind.
Eine solche kann dann innerhalb einer Funktion mittels global var_name
als global gekennzeichnet werden, wobei var_name der Name der Variablen
ist. |
[5] | Eine Bedingte Wertzuweisung in der Form
lambda x: a if condition else b ist allerdings erlaubt. |