Eine Funktion hat den Sinn, einen Block von Anweisungen auf Abruf verfügbar zu haben.
Es können Objekte als Argumente übergeben werden und ein oder mehrere Objekte als Ausgabe zurückgegeben werden.
Funktionen werden durch nach dem Variablennamen nachgestellte geschwungene Klammern "(...)
" aufgerufen
Beispiel: math.sqrt(2)
berechnet die Quadratwurzel aus 2.
In den Klammern stehen die sogenannten "Argumente" dieser Funktion, welche für die Auswertung übergeben werden -- werden keine Argumente übergeben, so bleiben die Klammern leer. Zurückgegeben wird das Ergebnis nach Ausführung des Codes in der Funktion.
Definiert werden Funktionen mittels
def name(argument1, argument2, ...):
und
return ...
gibt an, was zurückgegeben wird:
def meine_funktion(x, b):
y = x + b
z = 2*y + x
return z * y
Die Variable meine_funktion
beinhaltet das Funktionsobjekt, wobei die Hexadezimalzahl die (wenig informative) Speichadresse ist:
print(meine_funktion)
<function meine_funktion at 0x2b2b6ccf0b70>
Aufruf einer Funktion mittels Argumenten in runder Klammer:
meine_funktion(4.4, 11)
542.08
meine_funktion(-1, 2)
1
Werden Variablen als Argumente an die Funktion übergeben,
wird das Objekt auf das sie Verweisen übergeben -- das hat nichts mit dem Variablennamen zu tun.
Sprich, y
ist nun innerhalb der Funktion das x
und paul
wird zum y
.
Beispiel:
y = 3
paul = -1
meine_funktion(y, paul)
14
Eine weitere Art eine Funktion zu definieren ist ein "Lambda Ausdruck". Dies ist ein Einzeiler und erspart eine "ganze" Funktion schreiben zu müssen. Häufig werden diese Lambdaausdrücke an Funktion übergeben.
Beispiel:
f2 = lambda k : k**2 + 5
f2(2)
9
f2(-10)
105
Zwischenbemerkung:
Ganz allemein gilt für Python,
dass jedes Objekt
, welches aufgerufen werden kann,
durch die an den Variablennamen nachgestellten geschwungenen Klammern "(...)
" aufgerufen wird.
Der Überbegriff all dieser Objekte ist "aufrufbar" (engl. "callable"), und solche Funktionen sind prominente Vertreter davon. Später mehr ;-)
Die Rückgabe einer Funktion kann auch mehrstellig sein. Dies wird dann verwendet, um die Ergebnisse mehr als nur einer Berechnung zurückzugeben.
def mehrstellig(x):
k = 2 * x - 1
l = 90 - x
return k, l
mehrstellig(4)
(7, 86)
a, b = mehrstellig(10)
print(a)
print(b)
19 80
Im Detail gibt es verschiedene Arten wie diese aufrufbaren Objekte (Funktionen, ...) aufgerufen werden können. Dabei unterscheidet man
Des weiteren gibt es optional Werte für Schlüsselwortargumente, die einen Standardwert für das jeweilige Argument (engl. "default value") vorgeben. Dieser wird dann angenommen, wenn beim Aufruf dieses Argument nicht übergeben wird.
Im folgenden Beispiel wird eine Funktion mit den Argumenten "x", "y", "name" und "color" defininiert und anschließend auf unterschiedliche Arten aufgerufen.
def func(x, y, name, color="red"):
print("%s's position is %d.%d with color %s" % (name, x, y, color))
# 4-tes Argument ist die Farbe (position)
func(9, 0, "julia", "pink")
julia's position is 9.0 with color pink
# default color = red
func(4, 5, "joe")
joe's position is 4.5 with color red
func(1, 7, "jane", color="yellow")
jane's position is 1.7 with color yellow
# gemischte Reihenfolge
func(y = 11, color="grey", x = 3, name="jim")
jim's position is 3.11 with color grey
Darüber hinaus gibt es die Möglichkeit, die Argumente vorher in einer Liste oder Map abzuspeichern. Diese Objekte werden erst später im Kapitel über Datenstrukturen behandelt, werden aber zur Vollständigkeit hier erwähnt.
Es werden Variablen definiert, die die Werte für den Funktionsaufruf beinhalten.
Dann gibt der Stern "*
" bzw. Doppelstern "**
" an,
die Werte der jeweiligen Variablen als Argumente im Funktionsaufruf einzubauen.
Der einfache Stern steht für Positionsargumente (Listen) und der Doppelstern für Schlüsselwörter.
argumentliste = [5, 9, "jack"]
func(*argumentliste)
jack's position is 5.9 with color red
schluesselwoerter = {"name" : "jenny", "color" : "black" }
func(9, 2, **schluesselwoerter)
jenny's position is 9.2 with color black
ACHTUNG: es muss alles eindeutig bleiben
func(*argumentliste, **schluesselwoerter)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-20-19535a24702b> in <module>() ----> 1 func(*argumentliste, **schluesselwoerter) TypeError: func() got multiple values for argument 'name'
# KORREKTUR: weglassen des letzten Arguments in der liste "argumentliste"
func(*argumentliste[:-1], **schluesselwoerter)
jenny's position is 5.9 with color black
Diese Varianten des Funktionsaufrufs mögen eventuell etwas verwirren, sind jedoch dann sehr nützlich, wenn Argumente von Funktionen weitergereicht oder (leicht abgewandelt) wiederverwendet werden.
Funktionen können an Funktionen übergeben werden.
def f_inner(x):
k = 2 * x
return k + 1
def f_outer(func, v):
p = func(v + 1) - v
return p - 5
f_outer(f_inner, 10)
8
Ein Decorator ist eine Funktion, die eine Funktion auf eine Funktion abbildet. Er wird verwendet, um das Verhalten mehrerer Funktionen uniform zu beeinflussen indem er auf diese Funktionen angewendet wird.
Folgendes Beispiel definiert einen Decorator, welcher neben dem Ergebnis der Funktion auch die Argumente und den Typ der Rückgabe ausgibt.
def verbose(func):
def inner(*args, **kwargs):
print("args: %s" % str(args))
print("kwargs: %s" % str(kwargs))
result = func(*args, **kwargs)
print("return: %s" % type(result))
return result
return inner
@verbose
def f1(a, b):
return a + b
@verbose
def f2(word):
return word[::-1]
print(f1(5, 11))
args: (5, 11) kwargs: {} return: <class 'int'> 16
print(f2("hello"))
args: ('hello',) kwargs: {} return: <class 'str'> olleh
Die Functools) und Itertools) Bibliotheken beinhaltet mehrere Funktionen, um mit Funktionen arbeiten zu können bzw. um Operationen höherer Ordnung auf Funktionen anwenden zu können.
Praktisch erweisen sich hier auch all diejenigen Funktionen,
welche helfen iterative Berechnungen durch Funktionsaufrufe erledigen zu können.
Dies ist an die Konzepte aus der funktionalen Programmierung angelehnt.
Vertreter sind auch die Schlüsselwörter filter
, reduce
(bei Py3 in Itertools), zip
, ...
Ergebnis so einer Operation ist üblicherweise ein Iterator.
Dieser iteriert über die dadurch definierten Elemente,
und lassen sich mittels list( )
oder in einer for
-Schleife herausholen.
Dabei wird der Iterator "verbraucht", d.h. erneutes Abrufen der Elemente führt entweder dazu,
dass keine Elemente mehr vorhanden sind oder an der Stelle,
wo die Iteration aufgehört hat, weitergemacht wird.
Achtung: definiert der Iterator eine unendlich lange Liste von Elementen, führt das zu Schwierigkeiten: das Programm beendet nicht und der Arbeitsspeicher füllt sich.
import itertools as it
sumdivby7 = lambda x : sum(x) % 7 == 0
iter7 = filter(sumdivby7, zip(it.cycle([2, 3, 5, 7, 13, 17]), range(70)))
iter7
<filter at 0x2b2b6cd099b0>
list(iter7)
[(5, 2), (17, 11), (2, 12), (7, 21), (13, 22), (3, 25), (5, 44), (17, 53), (2, 54), (7, 63), (13, 64), (3, 67)]