Lektion 2

Listen

Oft ist es hilfreich, mehrere Werte zusammenfassen zu können, ohne für jeden Wert eine Variable definieren zu müssen. Insbesondere wenn man nicht von vornherein weiß, um wie viele Werte es sich handeln wird, kann man nicht mit einer Variablen pro Wert hantieren.

In Python gibt es zu diesem Zweck Listen. Eine Liste wird so notiert

a = [4, "Hallo", 5.0]
leereListe = []

Wie man am Beispiel sieht, können Listen beliebe Werte aufnehmen, auch wenn sie unterschiedliche Typen haben. Da Listen selbst nur Werte vom Typ “Liste” sind, können sie natürlich auch ihrerseits wieder Listen enthalten.

a = [4, [1, 2, [3]], 7]

Anstatt konkreter Werte, wie im Beispiel, können natürlich auch Variablen stehen.

Jetzt können wir Listen erzeugen. Bleibt zu klären, wie man mit ihnen arbeitet. Im Folgenden nehmen wir an, dass a eine Variable ist, die auf eine Liste verweist.

Auf das k-te Element der Liste greift man mit der Notation a[k-1] zu. Das erste Element ist folglich a[0], das siebzehnte a[16]. Auch negative Zahlen sind erlaubt, in diesem Fall wird von hinten gezählt. Das letzte Element der Liste ist a[-1], das siebente von hinten a[-7]. Wenn man nach einem Wert verlangt, der außerhalb des Listenlänge liegt, bekommt man eine Fehlermeldung. Die Länge einer Liste erfragt man mit len(a). Die Notation a[x] dient nicht nur dem Lesen, sondern auch dem Schreiben: a[5]=42 weißt dem sechsten Listenelement den Wert 42 zu, a[5] = [1,2,3] weißt dem sechsten Element den Wert [1,2,3] vom Typ Liste zu.

Man kann auch leicht Teillisten einer Liste erzeugen. Mit a[s:e] erzeugt man eine neue Liste, die die Werte [a[s],a[s+1],...,a[e-2],a[e-1]] enthält. Werden s oder e weggelassen, wird der Beginn, bzw. das Ende der Liste genommen. Die Notation ermöglicht auch das Ersetzen von Teillisten durch andere Werte. a[0:3]=[7] löscht die ersten drei Werte und ersetzt die Teilliste durch den Wert 7, a[0:3]=[1,2,3,4,5] ersetzt die ersten drei Werte durch fünf neue Werte (Achtung: nicht einen neuen Wert vom Typ Liste).

Bei der Verwendung von Listen ist Obacht geboten: Zuweisungen erzeugen keine Kopien, beide Variablen zeigen auf die selbe Liste und Manipulationen über die eine Variable werden beim Zugriff über die andere Variable widergespiegelt.

>>> a = [1,2,3,4,5]
>>> a
[1, 2, 3, 4, 5]
>>> b = a
>>> b
[1, 2, 3, 4, 5]
>>> b[3] = 123
>>> b
[1, 2, 3, 123, 5]
>>> a
[1, 2, 3, 123, 5]

Vergleiche dazu das Verhalten für einfache Typen

>>> a = 1
>>> a
1
>>> b = a
>>> b
1
>>> b = 2
>>> b
2
>>> a
1

Will man eine Kopie erzeugen, verwendet man die Syntax a[:] oder alternativ a[0:len(a)].

>>> a = [1,2,3,4,5]
>>> b = a[:]
>>> b[3] = 123
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 123, 5]

Mit Hilfe der a[s:e]=[1,2,3,4,5,6,7] Notation können wir bereits die Anzahl der Elemente in einer Liste verändern, aber typischerweise verwendet man dazu die Funktionen append und pop, die ein neues Element an das Ende der Liste anhängen, bzw. das letzte Element zurückliefern und entfernen. Die Verwendung ist so:

>>> a = [1,2,3,4,5]
>>> a.append(7)
>>> a
[1, 2, 3, 4, 5, 7]
>>> b = a.pop()
>>> a
[1, 2, 3, 4, 5]
>>> b
7

Listen verfügen noch über zahlreiche weitere Funktionen, die uns hier aber nicht interessieren sollen. Ein help(list) im Interpreter gibt einen kleinen Überblick.

Strings verhalten sich im Übrigen genauso wie Listen — man kann sie jedoch nicht verändern

>>> a = "Hallo Welt"
>>> a[3:7]
'lo W'
>>> a[2] = "e"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> a[2:7] = "asdf"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> a.append("a")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'append'

for-Schleifen

Mit den while-Schleifen kennen wir bereits ein wichtiges Konstrukt zum wiederholten Ausführen eines Blocks. for-Schleifen sind außerordentlich praktisch, wenn man über die Elemente in einer Liste iterieren möchte. Anstatt zu schreiben

i = 0
while i<len(a):
    print a[i]

können wir jetzt schreiben

for e in a:
    print e

Der Variablen e werden nacheinander die Werte aus der Liste zugewiesen und dann wird der Schleifenblock ausgeführt.

In diesem Zusammenhang ist die Funktion range praktisch, da sie es ermöglicht eine Liste von Zahlen zu erzeugen. Dabei verwendet man range(n), um eine Liste der Zahlen von 0 bis n–1 zu erzeugen. Weitere Benutzungsarten sind möglich, ein help(range) im Interpreter gibt Auskunft.

Jetzt, wo wir alle Schleifenkonstrukte in Python kennen, können wir auch die Befehle break und continue erwähnen. Innerhalb einer Schleife bricht break die Schleifenausführung ab und der Code nach der Schleife wird weiter ausgeführt. In der Regel will man break also mit Hilfe eines if nur unter bestimmten Bedingungen ausführen. Der Befehl continue bricht nicht die Ausführung der Schleife ab, sondern überspringt alle weiteren Anweisungen, die nach continue noch im Schleifenblock auftauchen.

Funktionen

Bisher können wir unseren Code nur schlecht strukturieren. Wenn wir einen Teil später noch einmal brauchen, können wir nichts besseres tun, als ihn an jene Stelle zu kopieren. Das fördert natürlich die Verbreitung von Fehlern und macht es schwieriger im Nachhinein noch Änderungen vorzunehmen. Außerdem erschwert diese Praxis die Lesbarkeit unserer Programme.

Wann immer ein Programmteil logisch zusammengehört, bietet es sich an, ihn auch explizit in einer Funktion zusammenzufassen. Wir haben schon ein paar Funktionen benutzt, ohne genau zu wissen, was Funktionen eigentlich sind, etwa raw_input() oder len() aber auch append() und pop(), wobei die letzten beiden etwas seltsame Syntax hatten, deren Bedeutung wir erst später kennenlernen.

Eine neue Funktion erzeugt man mit Hilfe des def Schlüsselwortes. Gefolgt wird es vom Namen der Funktion und einer Liste mit Argumenten. Der Funktionskörper ist dann ein neuer Block der beliebigen Code (einschließlich weiterer defs) enthalten kann. Funktionen werden aufgerufen, indem man ihren Namen und eine Reihe von Argumenten schreibt. Die Argumente verhalten sich in der Funktion wie neue lokale Variablen.


def f(a,b,c):
    print a
    print b
    print c

f("Hund","Katze","Maus")

Es ist natürlich auch möglich innerhalb einer Funktion eine weitere Funktion aufzurufen


def f(a,b,c):
    print a,b
    g(c)

def g(x):
    print x+1

Praktischerweise können Funktionen sich auch selbst aufrufen. Das nennt man Rekursion.


def printAll(n):
    if n<=0:
        print 1
    else:
        print n
        printAll(n-1)

Der aufmerksame Leser bemerkt sicherlich, dass man so die Funktion von Schleifen durch Funktionen simulieren kann (das ist wichtig in manchen anderen Programmiersprachen; Lektüre für Profis). Man sollte in der Regel aber lieber eine Schleife benutzen, wenn die Verständlichkeit des Programms dadurch nicht unnötig erschwert wird.

Funktionen können auch Werte zurückgeben. Das haben wir schon gesehen: len([1,2,3]) gibt die Länge der Liste zurück, raw_input() gibt den vom Nutzer eingegeben String zurück. Um in unseren eigenen Funktionen Werte zurückzugeben, verwenden wir den Befehl return. Dabei wird die Ausführung der Funktion mit return beendet und der Wert des folgenden Ausdrucks zurückgegeben.


def f(x):
    if x>3:
        return x-2
    else:
        return 2*x

In Python sind Funktionen auch Werte, die Variablen zugewiesen werden können.


def f(x):
    return x+1

g = f
print g(7)

Funktionen können also genau wie andere Werte im Programm herumgereicht werden. Insbesondere kann man sie in Listen speichern oder Funktionen als Argument übergeben.


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

def mal(a,b):
    return a*b

def rechnen(op,a,b,c):
    tmp = op(a,b)
    return op(tmp,c)

rechnen(mal,4,5,6)
rechnen(plus,4,5,6)