3Effektive Funktionen
3.1Pythons Funktionen sind erstklassig
Bei Python-Funktionen handelt es sich um Objekte erster Klasse. Du kannst sie Variablen zuweisen, in Datenstrukturen speichern, als Argumente an andere Funktionen übergeben und sogar als Werte von anderen Funktionen zurückgeben lassen. Ein intuitives Verständnis dieser Möglichkeiten macht es viel einfacher, sich mit anspruchsvolleren Python-Elementen wie Lambda-Funktionen und Dekoratoren vertraut zu machen. Es bringt dich auch auf den Weg zur Anwendung funktionaler Programmiertechniken.
Auf den folgenden Seiten stelle ich eine Reihe von Beispielen vor, mit deren Hilfe du ein solches intuitives Verständnis entwickeln kannst. Da die einzelnen Beispiele aufeinander aufbauen, solltest du sie in der angegebenen Reihenfolge lesen. Probiere sie auch in einer Session mit dem Python-Interpreter aus, während du den Erklärungen folgst. Es kann dich ein bisschen Zeit kosten, die hier vorgestellten Prinzipien zu verinnerlichen. Mache dir deswegen aber keine Sorgen, denn das ist völlig normal. Mir ist es genauso gegangen. Manchmal hatte ich das Gefühl, als käme ich nicht weiter, und dann machte es plötzlich klick, und alles ergab Sinn.
In diesem Abschnitt verwende ich zur Veranschaulichung die Funktion yell. Es handelt sich um eine einfache Beispielfunktion mit einer leicht wiedererkennbaren Ausgabe:
def yell(text):
return text.upper() + '!'
>>> yell('hello')
'HELLO!'
Funktionen sind Objekte
Alle Daten in einem Python-Programm werden durch Objekte oder Beziehungen zwischen Objekten dargestellt.1 Strings, Listen, Module und Funktionen sind Objekte. In Python sind auch Funktionen nichts Besonderes, sondern einfach Objekte. Da die Funktion yell in Python ein Objekt ist, kannst du sie wie jedes andere Objekt einer Variablen zuweisen:
In dieser Zeile wird die Funktion nicht aufgerufen. Stattdessen wird hier der neue Name bark erstellt, der auf das Funktionsobjekt zeigt, auf das yell verweist. So kannst du dasselbe zugrunde liegende Funktionsobjekt jetzt auch dadurch ausführen, dass du bark aufrufst:
Funktionsobjekte und ihre Namen sind zwei verschiedene Dinge. Wenn du etwa den ursprünglichen Namen der Funktion löschst (yell), zeigt der alternative Name (bark) nach wie vor auf die zugrunde liegende Funktion, sodass du diese dadurch nach wie vor aufrufen kannst:
>>> del yell
>>> yell('hello?')
NameError: "name 'yell' is not defined"
>>> bark('hey')
'HEY!'
Wenn eine Funktion erstellt wird, verknüpft Python sie zu Debuggingzwecken mit einer ID in Stringform. Diese interne ID kannst du mit dem Attribut __name__ abrufen:2
Der interne Funktionsname in __name__ ist zwar nach wie vor yell. Das hat aber keine Bedeutung dafür, wie du im Code auf das Funktionsobjekt zugreifst. Diese ID ist lediglich eine Hilfe für das Debugging. Eine Variable, die auf eine Funktion zeigt, und die Funktion selbst sind zwei völlig verschiedene Dinge.
Funktionen in Datenstrukturen speichern
Da Funktionen Objekte erster Klasse sind, lassen sie sich wie alle anderen Objekte in Datenstrukturen speichern. So kannst du sie unter anderem einer Liste hinzufügen:
>>> funcs = [bark, str.lower, str.capitalize]
>>> funcs
[<function yell at 0x10ff96510>,
<method 'lower' of 'str' objects>,
<method 'capitalize' of 'str' objects>]
Auch der Zugriff auf die Funktionsobjekte in der Liste erfolgt wie bei jeder anderen Art von Objekt:
>>> for f in funcs:
... print(f, f('hey there'))
<function yell at 0x10ff96510> 'HEY THERE!'
<method 'lower' of 'str' objects> 'hey there'
<method 'capitalize' of 'str' objects> 'Hey there'
Es ist sogar möglich, ein Funktionsobjekt in einer Liste aufzurufen, ohne es erst einer Variablen zuzuweisen. Du kannst es nachschlagen und dann im selben Ausdruck unmittelbar das körperlose Funktionsobjekt aufrufen:
>>> funcs[0]('heyho')
'HEYHO!'
Funktionen an andere Funktionen übergeben
Da Funktionen Objekte sind, kannst du sie auch als Argumente an andere Funktionen übergeben. Die folgende Funktion namens greet formatiert einen Begrüßungsstring mithilfe des Funktionsobjekts, das ihr übergeben wurde, und gibt ihn dann aus:
def greet(func):
greeting = func('Hi, I am a Python program')
print(greeting)
Die resultierende Begrüßung kannst du dadurch gestalten, dass du unterschiedliche Funktionen übergibst. Das folgende Beispiel zeigt, was geschieht, wenn du dazu die Funktion bark nimmst:
>>> greet(bark)
'HI, I AM A PYTHON PROGRAM!'
Natürlich kannst du auch eine neue Funktion definieren, um eine andere Art von Begrüßung zu generieren. Wenn du nicht willst, dass deine Python-Programme wie Optimus Prime klingen, kann die im Folgenden gezeigte Funktion whisper für deine Zwecke besser geeignet sein:
def whisper(text):
return text.lower() + '...'
>>> greet(whisper)
'hi, i am a python program...'
Funktionsobjekte als Argumente an andere Funktionen übergeben zu können, bietet viele Möglichkeiten. Du kannst damit Verhalten in einem Programm kapseln und austauschen. In diesem Beispiel kannst du die Ausgabe der Funktion greet dadurch verändern, dass du unterschiedliche Begrüßungsverhalten übergibst, während die Funktion selbst unverändert bleibt.
Funktionen, die andere Funktionen als Argumente entgegennehmen, werden auch als Funktionen höherer Ordnung bezeichnet. Für die funktionale Programmierung sind sie unentbehrlich. Das klassische Beispiel für eine Funktion höherer Ordnung ist die integrierte Python-Funktion map. Sie nimmt ein Funktionsobjekt und ein iterierbares Objekt entgegen und ruft dann die Funktion für jedes Element in dem iterierbaren Objekt auf, wobei sie jeweils die Ergebnisse zurückgibt. Das folgende Beispiel zeigt, wie du mehrere Begrüßungen auf einmal ausgibst, indem du die Funktion bark an map übergibst:
>>> list(map(bark, ['hello', 'hey', 'hi']))
['HELLO!',...