Python unter Linux: Funktionen

Funktionen gliedern den Programmtext, gestalten den Code übersichtlich und ersparen dem Programmierer wertvolle Entwicklungszeit. Ebenfalls sind sie eine sehr gute Schnittstelle, um im Team gemeinsam an Aufgaben zu arbeiten.

Funktionen

Bearbeiten

Funktionen werden wie folgt definiert und aufgerufen:

#!/usr/bin/python

def HalloWelt():
    print "Hallo, Welt!"

HalloWelt()
HalloWelt()
  Ausgabe

user@localhost:~$ ./funk1.py
Hallo, Welt!
Hallo, Welt!

Dem Schlüsselwort def folgt der Funktionsname. Die Anweisungen der Funktion folgt als Block. Funktionen können Parameter haben und diese nutzen:

#!/usr/bin/python

def HalloWelt(anzahl):
    if anzahl > 0:
        for i in xrange(0, anzahl):
            print "Hallo, Welt!"

HalloWelt(3)
  Ausgabe

user@localhost:~$ ./funk2.py
Hallo, Welt!
Hallo, Welt!
Hallo, Welt!

Eine der Aufgaben von Funktionen ist es, Werte zurückzuliefern, wobei jeder Typ zurückgegeben werden kann. Wir beschränken uns in den Beispielen auf Zahlen:

#!/usr/bin/python

def summe(a, b, c):
    wert = a + b + c
    return wert

print summe(1, 7, 3)
  Ausgabe

user@localhost:~$ ./funk3.py
11

Die folgende Funktion berechnet für jeden String die Buchstabensumme, wobei ein A für 1 steht, B für 2 und so fort. Kleinbuchstaben werden zunächst in Großbuchstaben umgewandelt. Danach wird die Summe ermittelt, wobei nur die Buchstaben A-Z gezählt werden:

#!/usr/bin/python
def StringWert(s):
    s = s.upper()
    summe = 0
    for zeichen in s:
        wert = ord(zeichen) - ord('A') + 1
        if wert > 0 and wert <= 26:
            summe += wert
    return summe

print StringWert("ABBA")
print StringWert("Hallo, Welt!")
  Ausgabe

user@localhost:~$ ./funk4.py
6
108

Die Methode upper() kennen Sie schon, sie liefert einen String zurück, welcher nur aus Großbuchstaben besteht. Über diesen String wird iteriert. Mit ord() erhalten wir einen Zahlenwert für den aktuellen Buchstaben. Von diesem Zahlenwert ziehen wir den Zahlenwert des ersten Buchstabens (A) ab, und da wir nicht von 0, sondern von 1 beginnen wollen, müssen wir zur Werteberechnung noch 1 hinzu addieren.

Funktionen kann man auch ineinander schachteln, wie folgendes Beispiel zeigt:

#!/usr/bin/python

def Hypothenuse(Kathete1, Kathete2):
    def Quadriere(x):
        return x * x

    def Summiere(a, b):
        return a + b

    Hyp = Summiere(Quadriere(Kathete1), Quadriere(Kathete2))
    return Hyp
    
print "Das Hypothenusenquadrat lautet:", Hypothenuse(1, 2)
  Ausgabe

user@localhost:~$ ./funk5.py
Das Hypothenusenquadrat lautet: 5

Außerhalb der Funktion Hypothenuse() sind die Funktionen Quadriere() und Summiere() unbekannt.

Funktionsdokumentation

Bearbeiten

Funktionen können und sollten dokumentiert werden. Schreibt man mit passender Einrückung einen Doc-String, also einen anonymen mehrzeiligen String, in die Funktion, so kann man an anderer Stelle auf ihn Bezug nehmen. Es werden für jedes angelegte Objekt einige Zusatzinformationen generiert und abgespeichert.

#!/usr/bin/python
# -*- coding: utf-8 -*-

def Collatz(parm):
    """Bestimmt das nächste Collatz-Folgeelement vom Parameter parm.
    Das nächste Folgeelement ist 3*parm + 1, wenn parm ungerade ist,
    sonst parm/2 """
    if parm % 2 != 0:
        return 3 * parm + 1
    else:
        return parm / 2

print Collatz.__doc__
  Ausgabe

user@localhost:~$ ./fdoc1.py
Bestimmt das nächste Collatz-Folgeelement vom Parameter parm.
  Das nächste Folgeelement ist 3*parm + 1, wenn parm ungerade ist,
  sonst parm/2

Mit __doc__ wird auf eine automatisch erstellte und belegte Variable zugegriffen. Diese Variable wird erzeugt, wenn die Funktion angelegt wird. Falls keine Dokumentation angegeben wurde, enthält __doc__ einen leeren String.

Parameter

Bearbeiten

Nachdem wir nun die Grundlagen der Funktionen behandet haben, wollen wir uns detailliert mit Parametern beschäftigen.

Parameter kann man mit Werten vorbelegen:

#!/usr/bin/python

def HalloWelt(anzahl=3):
    print
    if anzahl > 0:
        for i in xrange(0, anzahl):
            print "Hallo, Welt!"
                        
HalloWelt(1)
HalloWelt()
  Ausgabe

user@localhost:~$ ./param1.py
 
Hallo, Welt!
 
Hallo, Welt!
Hallo, Welt!
Hallo, Welt!

Man gibt hierbei in der Parameterliste einen voreingestellten Wert an, den man beim Funktionsaufruf auch überschreiben kann. Hat man mehrere Parameter, kann man einzelne von ihnen vorbelegen und im konkreten Aufruf auch vertauschen:

#!/usr/bin/python

def summe(a=3, b=2):
    return a + b

print summe()
print summe(a=4)
print summe(b=4)
print summe(b=2, a=1)
  Ausgabe

user@localhost:~$ ./param2.py
5
6
7
3

Variable Parameter

Bearbeiten

Gerade bei Funktionen wie der Summenberechnung ist es praktisch, eine variable Anzahl an Parametern zu haben. Dadurch werden recht praktische Funktionen möglich, und das schreiben neuer Funktionen für jede Anzahl an Parametern entfällt:

#!/usr/bin/python

def summe(*list):
    s = 0
    for element in list:
        s += element
    return s

print summe()
print summe(1)
print summe(1, 2, 3, 4, 5, 6, 7, 8, 9)
  Ausgabe

user@localhost:~$ ./varparam1.py
0
1
45

Der Parameter *list ist hierbei ein Tupel, das abhängig von der Anzahl der im Aufruf erfolgten Argumente entweder leer (()) ist, oder die Argumente (1) und (1, 2, 3, 4, 5, 6, 7, 8, 9) enthält. Anschließend brauchen wir zur Summenberechnung nur noch über dieses Tupel zu iterieren.

Neben der Tupel-Form variabler Argumente gibt es noch die Dictionary-Form:

#!/usr/bin/python

def ZeigeListe(**liste):
    print liste

ZeigeListe(a1=1, a2=2, a3=3)
ZeigeListe(Name="Schmitz", Vorname="Elke", Postleitzahl=44444)
  Ausgabe

user@localhost:~$ ./varparam2.py
{'a1': 1, 'a3': 3, 'a2': 2}
{'Name': 'Schmitz', 'Vorname': 'Elke', 'Postleitzahl': 44444}

Hier wird lediglich ein Dictionary aufgebaut, welches jeweils aus dem Argumentenwort und -wert besteht.

Globale und lokale Variablen

Bearbeiten

Haben funktionslokale Variablen den gleichen Namen wie Variablen, die außerhalb der Funktion definiert wurden, so werden globalere Variablenwerte weder lesend noch schreibend beim Zugriff berührt:

#!/usr/bin/python

wert = 42
print wert

def wertetest():
    wert = 12
    print wert

wertetest()
print wert
  Ausgabe

user@localhost:~$ ./global1.py
42
12
42

Neue Variablen werden so lokal es geht erzeugt. Hat eine neue Variable innerhalb einer Funktion den gleichen Namen wie eine andere Variable außerhalb, so wird nur die innere Variable genutzt.

Möchte man es anders haben, muss man explizit den Zugriff auf die globale Variable anfordern:

#!/usr/bin/python

wert = 42
print wert

def wertetest():
    global wert
    wert = 12
    print wert

wertetest()
print wert
  Ausgabe

user@localhost:~$ ./global2.py
42
12
12

Das Schlüsselwort global sorgt hier für den Zugriff auf die außerhalb der Funktion definierte globale Variable. Bitte beachten Sie, dass Zugriffe auf globale Variablen die Lesbarkeit des Codes vermindern.

Funktionen auf Wertemengen

Bearbeiten

Hat man eine Funktion geschrieben, die ein einzelnes Argument verarbeitet und möchte diese Funktion nun auf eine ganze Liste von Werten anwenden, so bietet sich die Funktion map an. Diese nimmt ein Funktionsargument wie auch eine Liste auf, wendet die Funktion auf jedes Element dieser Liste an und gibt eine Liste als Ergebnis zurück. Folgendes Beispiel verdeutlicht dies:

#!/usr/bin/python

def quadriere(x):
    return x * x

quadratzahlen = map(quadriere, [1, 2, 3, 4, 5, 6])
print quadratzahlen
  Ausgabe

user@localhost:~$ ./map1.py
[1, 4, 9, 16, 25, 36]

Die Funktion quadriere() berechnet für jedes Element der Liste von 1 bis 6 die Quadratzahl und gibt eine Liste mit Quadratzahlen zurück. Selbstverständlich kann dieses konkrete Problem auch mit Hilfe einer for-Schleife gelöst werden, was man benutzt ist meist mehr eine Geschmacksfrage.

Eine mit lambda erzeugte Funktion ist anonym, sie hat keinen Namen und wird nur in einem bestimmten Kontext genutzt, wie zum Beispiel mit map:

#!/usr/bin/python

quadratzahlen = map(lambda x: x * x, [1, 2, 3, 4, 5, 6])
print quadratzahlen
  Ausgabe

user@localhost:~$ ./lambda1.py
[1, 4, 9, 16, 25, 36]

Nach dem Schlüsselwort lambda folgt bis zum Doppelpunkt eine durch Kommata getrennte Aufzählung von Argumenten, hinter dem Doppelpunkt beginnt die Anweisung. lambda-Funktionen lassen sich auch nutzen, um während des Programmlaufes neue Funktionen zu erzeugen. Das folgende Beispiel demonstriert, wie eine Quadrat- und eine Wurzelfunktion neu erzeugt werden:

#!/usr/bin/python

def Exponential(z):
    return lambda x: x**z

quadriere = Exponential(2)
wurzel = Exponential(0.5)

a = quadriere(2)
print a
b = wurzel(a)
print b
  Ausgabe

user@localhost:~$ ./lambda2.py
4
2.0

Die Funktion Exponential() erwartet ein Argument, mit dem die lambda-Funktion erzeugt wird. Die Funktion gibt nicht etwa den Wert dieser neuen Funktion zurück, sondern die neue Funktion selbst. So erzeugt quadriere = Exponential(2) eine neue Quadratfunktion, die man auch sogleich anwenden kann.

Listen erzeugen sich selbst

Bearbeiten

Wo wir gerade dabei waren, mit map() Listen zu erzeugen, wird vielleicht auch folgende Syntax[1] etwas für Sie sein. Lehnen Sie sich zurück und genießen Sie die unglaubliche Vorstellung, wie eine Liste sich selbst erzeugt:

#!/usr/bin/python

liste = [x * x for x in xrange(1, 10)]
print liste
  Ausgabe

user@localhost:~$ ./comprehension1.py
[1, 4, 9, 16, 25, 36, 49, 64, 81]

Diese Liste wird aufgebaut, indem alle Werte, die xrange() liefert, quadriert werden. Wir haben es hier also wieder mit einer Liste von Quadratzahlen zu tun. Anders als bei der for-Schleife steht hier der Funktionskörper vor dem Schleifeniterator. Diese Code ist aber noch nicht alles, was wir Ihnen bieten können:

#!/usr/bin/python

liste = [x * x for x in xrange(1, 10) if x % 2 == 0]
print liste
  Ausgabe

user@localhost:~$ ./comprehension2.py
[4, 16, 36, 64]

Nun haben wir es mit einer Liste von Quadratzahlen zu tun, die aus der Menge der geraden Zahlen gebildet wurden. Das in der Liste nachgestellte if sorgt hier für eine Auswahl der Werte, die in die Vorschrift zur Listenbildung übernommen werden.

Um alle Dreier-Tupel einer Liste auszugeben, also alle Kombinationen einer dreielementigen Liste aufzuzählen, dient folgendes Programm:

#!/usr/bin/python

Liste = ['1', '2', '+']
Kreuz = [(a, b, c)  for a in Liste for b in Liste for c in Liste]
print Kreuz
  Ausgabe

user@localhost:~$ ./comprehension3.py
[('1', '1', '1'), ('1', '1', '2'), ('1', '1', '+'), ('1', '2', '1'), ('1', '2', '2'), ('1', '2', '+'), ('1', '+', '1'), ('1', '+', '2'), ('1', '+', '+'), ('2', '1', '1'), ('2', '1', '2'), ('2', '1', '+'), ('2', '2', '1'), ('2', '2', '2'), ('2', '2', '+'), ('2', '+', '1'), ('2', '+', '2'), ('2', '+', '+'), ('+', '1', '1'), ('+', '1', '2'), ('+', '1', '+'), ('+', '2', '1'), ('+', '2', '2'), ('+', '2', '+'), ('+', '+', '1'), ('+', '+', '2'), ('+', '+', '+')]

Wie wir sehen, können wir Listen aus Tupel aus anderen Listen erzeugen.


Solche Listen können auch mit der Funktion filter erzeugt werden. Dieser übergibt man eine Funktion, die für Argumente bool'sche Werte zurückliefert und eine Liste, über die iteriert werden soll. Zurück erhält man eine Liste mit all jenen Werten, für die die Funktion True liefert:

#!/usr/bin/python

def durch3teilbar(x):
  return x % 3 == 0

print filter(durch3teilbar, range(10))
  Ausgabe

user@localhost:~$ ./comprehension4.py
[0, 3, 6, 9]

Die Funktion durch3teilbar() ergibt True für alle Werte, die durch drei teilbar sind. Nur noch diese Werte verbleiben in der übergebenen Liste.

Generatoren

Bearbeiten

Funktionen wie range() und xrange() erzeugen Objekte, über die sich iterieren lässt, im einfachsten Fall Listen oder Tupel. Eine andere Art iterierbarer Objekte sind Generatoren, um die es hier geht.

Generatoren mit yield

Bearbeiten

Ein Generator wird wie eine Funktion erzeugt und erstellt eine Folge von Werten. Einen Generator kennen Sie bereits, nämlich xrange(). Die Folge wird elementeweise bereitgestellt mit yield():

#!/usr/bin/python

def MeinGenerator():
    yield(1)
    yield(2)
    yield(3)

for i in MeinGenerator():
    print i
  Ausgabe

user@localhost:~$ ./yield1.py
1
2
3

yield() liefert beim ersten Aufruf des Generators den ersten Wert zurück und stoppt dann die Ausführung. Es wird hierbei also keine Liste erzeugt, sondern jeder Zugriff auf den Generator liefert den nächsten von yield() bereitgestellten Wert.

Werte in Generatoren lassen sich bequem in for-Schleifen erzeugen:

#!/usr/bin/python

def rueckwaerts(text):
    length = len(text)
    for i in xrange(length):
        yield(text[length - i - 1])

for c in rueckwaerts("Hallo, Welt!"):
    print "\b%c" % c,

print
  Ausgabe

user@localhost:~$ ./yield2.py
!tleW ,ollaH

Hier wird der Text rückwärts ausgegeben, wobei yield() angefangen vom letzten Zeichen jedes Zeichen des Textes zurückliefert. Da das Komma bei der print-Anweisung ein Leerzeichen einfügt, müssen wir mit einem Backspace (\b) dafür sorgen, dass dieses wieder entfernt wird.

 

Tipp:
Die oben benutzte Funktion dient als einfaches Beispiel zur Demonstration von Generatoren. Wenn Sie sich aber fragen, wie eine Zeichenkette einfach rückwärts dargestellt werden kann, dann hängen Sie [::-1] an eine String-Variable oder an ein String-Literal. Das gleiche Ergebnis wie oben wäre über "Hallo, Welt!"[::-1] ebenfalls möglich.


Generatorexpressions

Bearbeiten

Auch für Generatoren gibt es wieder eine abkürzende Schreibweise:

#!/usr/bin/python

genex = (i * i for i in xrange(5))

for wert in genex:
    print wert
  Ausgabe

user@localhost:~$ ./genex1.py
0
1
4
9
16

Die Syntax ist ähnlich wie bei List Comprehensions, jedoch werden runde Klammern verwendet.

Zusammenfassung

Bearbeiten

Wir haben gezeigt, wie man Funktionen definiert, Werte zurückgibt und Funktionen mit variablen Parameterlisten schreibt. Der Gebrauch von lokalen und globalen Variablen wurde erläutert wie auch die Anwendung der Funktionen auf Listen mit Hilfe von map(). Als Syntaxzucker gaben wir einen Einblick in anonyme Funktionen. List Comprehensions und Generatoren rundeten das Thema ab.

Anmerkungen

Bearbeiten
  1. Diese Syntax nennt man " List Comprehension "