Datenstrukturen und Datentypen
In diesem Kapitel werden einige Dinge, die bereits in vorherigen Kapiteln beschrieben wurden, ausführlicher erklärt. Und es kommen auch einige neue Datenstrukturen und ergänzende Informationen hinzu.
mehr zu Listen
Der Datentyp Liste hat einige weitere Methoden. Im Folgenden alle Methoden von Listenobjekten:
Hinweis: In den folgenden Beispielen - und vielen anderen Beispielen in der Python-Dokumentation - kennzeichnen eckige Klammern innerhalb eines Methodenaufrufs ein optionales Argument. So bedeutet z.B. a.pop([i])
, dass die Methode pop
von a
entweder mit einem Argument wie a.pop(4)
oder ohne a.pop()
aufgerufen werden kann.
list.append(x)
: fügt ein Elementx
am Ende der Liste hinzu. Äquivalent zua[len(a):] = x
.list.extend(iterable)
: Erweitert die Liste um alle Elemente aus der Sequenziterable
. Äquivalent zua[len(a):] = iterable
.list.insert(i, x)
: Fügt ein Elementx
vor dem Element an Positioni
der Liste hinzu.list.insert(0, x)
würde also das Elementex
an den Anfang der Liste einsetzen.list.insert(len(list), x)
ist äquivalent zulist.append(x)
.list.remove(x)
: Entfernt das erste Element aus der Liste, dessen Wert gleichx
ist. Wird kein Element gefunden, erhält man einenValueError
.list.pop([i])
: Entfernt das Element an der Positioni
aus der Liste und liefert es als Rückgabewert zurück. Wenn nurlist.pop()
ohne Index angegeben wird, wird das letzte Element aus der Liste entfernt.list.clear()
: Entfernt alle Elemente aus der Liste, sodass diese leer ist. Äquivalent zudel list[:]
.list.index(x[, start[, end]])
: Gibt den Index des ersten Elements zurück, dessen Wert gleichx
ist. Wird kein Element gefunden erhält man einenValueError
. Gibt man die optionalen Elementestart
undend
an wird nur in dem Abschnitt zwischen den Indizesstart
undend
gesucht. Wird ein Element in dem Abschnitt gefunden wird dessen Index relativ zur gesamten Liste angegeben, nicht relativ zum Index des Starts.list.count(x)
: Liefert die Anzahl der Vorkommen vonx
in der Liste zurück.list.sort(*, key=None, reverse=False)
: Sortiert die Liste in-situ, d.h. es wird keine neue Liste zurückgeliefert, sondern die Liste an sich wieder sortiert. Die möglichen Argumente und Optionen sind identisch mit der Built-In Funktion sorted.list.sort(*, key=None, reverse=False)
: Sortiert die Liste in-situ, d.h. es wird keine neue Liste zurückgeliefert, sondern die Liste an sich wieder sortiert. Die möglichen Argumente und Optionen sind identisch mit der der Built-In Funktion sorted.list.reverse()
: Kehrt die Reihenfolge der Elemente in der Liste um. Die Liste wird direkt umgekehrt, es wird keine neue Liste zurückgegeben.list.copy()
: Liefert eine flache Kopie der Liste zurück. Äquivalent zulist[:]
.
Ein Beispiel, das die meisten der Listenmethoden verwendet:
>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4) # Find next banana starting at position 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'
Wie oben bereits erwähnt sind Methoden wie insert
, remove
oder sort
solche, die die Liste verändern und keinen Rückgabewert ausgeben - sie liefern den Standardwert None
. Dies ist grundsätzliches Designprinzip für alle veränderbaren Datenstrukturen in Python.
Eine andere erwähnenswerte Sache ist, dass nicht alle Daten sortiert oder verglichen werden können. Zum Beispiel kann [None, 'hello', 10]
nicht sortiert werden, weil Ganzzahlen nicht mit Zeichenketten und None
nicht mit anderen Typen verglichen werden können. Außerdem gibt es einige Typen, die keine definierte Ordnungsbeziehung haben. Zum Beispiel komplexe Zahlen. So ist 3+4j < 5+7j
kein gültiger Vergleich.
Listen als Stacks
Die Listenmethoden machen es sehr einfach, eine Liste als Stack (auf Deutsch: Stapelspeicher oder kurz: Stapel) zu verwenden, wobei das zuletzt hinzugefügte Element das erste Element ist, welches abgerufen wird ("last-in, first-out", kurz: LiFo). Um ein Element auf dem Stack abzulegen wird list.append
verwendet. Um ein Element vom Stack zu nehmen wird list.pop
ohne expliziten Index verwendet. Zum Beispiel:
>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]
Listen als Queues
Es ist auch möglich, eine Liste als Queue (auf Deutsch: Warteschlange zu verwenden, bei der das erste hinzugefügte Element auch das erste abgefragte Element ist ("first-in, first-out", kurz: FiFo). Allerdings sind Listen für diesen Zweck nicht effizient. Während append
und pop
am Ende einer Liste schnell sind, sind insert
oder pop
am Anfang einer Liste vergleichsweisen langsam. Dies ist durch die interne Art und Weise bedingt, wie Python Listen intern speichert. Bei einem insert
oder pop
am Anfang muss die komplette Liste intern neu geschrieben werden.
Um eine Queue zu implementieren, verwendet man besser collections.deque, die für schnelles append
und pop
von beiden Enden aus konzipiert wurde. "deque" (gesprochen wie das deutsche Wort "Deck") ist die Kurzform für "double-ended queue". Beispiel:
>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry") # Terry arrives
>>> queue.append("Graham") # Graham arrives
>>> queue.popleft() # The first to arrive now leaves
'Eric'
>>> queue.popleft() # The second to arrive now leaves
'John'
>>> queue # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])
List Comprehensions
List Comprehensions bieten eine kompakte Möglichkeit Listen zu erstellen. Übliche Anwendungen sind die Erstellung neuer Listen bei denen jedes Element das Ergebnis einer oder einiger Operation(en) ist, die auf jedes Element einer anderen Sequenz oder Iterables angewandt werden. Eine weitere Anwendung ist die Erstellung einer Teilfolge von Elementen, die eine bestimmte Bedingung erfüllen.
Wenn z.B. eine Liste mit den Quadratzahlen von 1 bis 9 angelegt werden soll, geht das konventionell so:
>>> squares = []
>>> for i in range(1, 10):
... squares.append(i**2)
...
>>> squares
[1, 4, 9, 16, 25, 36, 49, 64, 81]
Zu beachten ist, dass dies eine Variable mit dem Namen i
erzeugt (oder überschreibt), die auch nach Beendigung der Schleife noch existiert. Man kann die Liste der Quadrate ohne jegliche Nebeneffekte berechnen, und zwar wie folgt:
>>> squares = list(map(lambda i: i**2, range(1, 10)))
Mittels List Comprehension funktioniert das wie folgt:
>>> squares = [x**2 for x in range(1, 10)]
List Comprehensions sind im Vergleich zu den beiden zuvor genannten Wegen kompakter und besser lesbar.
Eine List Comprehension besteht aus Klammern die einen Ausdruck enthalten, gefolgt von einer for
Klausel, dann null oder mehr for
oder if
Klauseln. Das Ergebnis ist eine neue Liste, die sich aus der Auswertung des Ausdrucks im Kontext der nachfolgenden for
und if
Klauseln ergibt. Zum Beispiel kombiniert dieser List Comprehension die Elemente zweier Listen, wenn sie nicht gleich sind:
>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
Konventionell geschrieben sähe dies so aus:
>>> combs = []
>>> for x in [1,2,3]:
... for y in [3,1,4]:
... if x != y:
... combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
Zu beachten ist, dass die Reihenfolge der for
und if
Anweisungen in diesen beiden Ausschnitten gleich ist.
Wenn der Ausdruck ein Tupel ist (z.B. (x, y)
wie im vorherigen Beispiel), muss er in Klammern gesetzt werden:
>>> vec = [-4, -2, 0, 2, 4]
>>> # neue Liste generieren, in der alle Elemente von vec verdoppelt sind
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filtern der Liste zum Ausschluss negativer Zahlen
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # eine Funktion, hier die Built-In Funktion abs() aus alle Elemente anwenden
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # für jedes Element die strip() Methode von Strings anwenden
>>> freshfruit = [' banana', ' loganberry ', 'passion fruit ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # eine Liste aus 2er-Tupel der Struktur (number, square) generieren
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # das Tupel mit in Klammern stehen, sonst gibt es einen Fehler
>>> [x, x**2 for x in range(6)]
File "<stdin>", line 1
[x, x**2 for x in range(6)]
^^^^^^^
SyntaxError: did you forget parentheses around the comprehension target?
>>> # eine flache Liste mit List Comprehension erzeugen unter Verwendung von zwei 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
List Comprehensions können komplexe Ausdrücke und verschachtelte Funktionen enthalten:
>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
List Comprehensions gelten im Allgemeinen als "pythonisch" und werden entsprechend häufig verwendet bzw. es gilt allgemein als empfehlenswert, List Comprehensions an passenden Stellen im Code zu verwenden.
verschachtelte List Comprehensions
Der Anfangsausdruck einer List Comprehension kann ein beliebiger Ausdruck sein, einschließlich einer anderen List Comprehension. Im folgenden Beispiel wird eine 3x4-Matrix, die als eine Liste von 3 Listen der Länge 4 implementiert ist, generiert:
>>> matrix = [
... [1, 2, 3, 4],
... [5, 6, 7, 8],
... [9, 10, 11, 12],
... ]
Die folgende List Comprehension transformiert die Liste, d.h. es werden Spalten und Reihen vertauscht:
>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Wie im vorigen Abschnitt gezeigt wird die innere List Comprehension im Kontext des nachfolgenden for
ausgewertet. Dieses Beispiel ist äquivalent zu:
>>> transposed = []
>>> for i in range(4):
... transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
Was wiederum das gleich ist wie:
>>> transposed = []
>>> for i in range(4):
... # the following 3 lines implement the nested listcomp
... transposed_row = []
... for row in matrix:
... transposed_row.append(row[i])
... transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
In der realen Programmierwelt sollte man integrierte Funktionen komplexen Ablaufanweisungen vorziehen. Die Funktion zip würde sich für diesen Anwendungsfall sehr gut eignen:
>>> list(zip(*matrix))
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
die del Anweisung
Es gibt eine Möglichkeit, ein Element aus einer Liste zu entfernen. Dazu dient die del Anweisung in Kombination mit der Angabe des Indexes des Elements, das man entfernen möchte. del
steht für delete
, auf Deutsch: entfernen, löschen. Dies unterscheidet sich von der Methode list.pop
, die einen Wert zurückgibt. Die Anweisung del
kann auch verwendet werden, um Slices aus einer Liste zu entfernen oder die gesamte Liste zu löschen (was zuvor durch Zuweisung einer leeren Liste an den Slice gemacht wurde). Zum Beispiel:
>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]
del
kann auch dazu verwendet werden, um Variablen zu löschen:
>>> del a
Den Namen a
im Folgenden zu referenzieren liefert einen Fehler - zumindest bis ihm ein anderer Wert zugewiesen wird. Später werden noch andere Anwendungen von del
gezeigt.
Hinweis: del
löscht die Referenz auf ein Objekt aber nicht das Objekt selber! del
kann keine Objekte löschen. Dies ist ein subtiler Unterschied, der zwar im Rahmen diese Tutorials keine Rolle spielt, aber später bei fortgeschrittener Python-Programmierung gegebenenfalls einmal relevant werden könnte. Hat man so etwas wie
>>> a = 'Python'
>>> del a
würde del a
die Referenz von a
auf den String "Python" aus dem aktuellen Namensraum entfernen, das String-Objekt "Python" ist aber noch im Speicher des Rechners. Wenn es sonst keine weiteren Referenzen auf a
im aktuellen Namensraum gibt, würde der nächste Durchlauf des Garbage Collectors von Python das String-Objekt löschen.
Tupel und Sequenzen
Es wurde schon gezeigt, dass Listen und Zeichenketten viele gemeinsame Eigenschaften haben, z. B. Index- und Slicing-Operationen. Dies sind zwei Beispiele für Sequenz-Datentypen (siehe Sequenz Typen — list, tuple, range). Da Python eine sich entwickelnde Sprache ist, könnten weitere Sequenz-Datentypen hinzugefügt werden. Es gibt auch einen anderen Standard-Sequenz-Datentyp: das Tupel (auf Englisch: tuple).
Ein Tupel besteht aus einer Reihe von Werten, die durch Kommas getrennt sind, zum Beispiel:
>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Tupels können verschachtelt sein:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Tupels sind immutable (unveränderlich):
... t[0] = 88888
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> # but they can contain mutable objects:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])
Wie zu sehen ist, werden Tupel bei der Ausgabe immer in Klammern eingeschlossen, damit verschachtelte Tupel korrekt interpretiert werden. Sie können mit oder ohne umgebende Klammern eingegeben werden, obwohl Klammern oft ohnehin notwendig sind, z.B. wenn das Tupel Teil eines größeren Ausdrucks ist. Es ist nicht möglich, den einzelnen Elementen eines Tupels Zuweisungen zuzuweisen. Es ist jedoch möglich, Tupel zu erstellen, die veränderbare Objekte enthalten, z. B. Listen.
Obwohl Tupel auf den ersten Blick ähnlich wie Listen aussehen, werden sie oft in unterschiedlichen Situationen und für unterschiedliche Zwecke verwendet. Tupel sind "immutable" (=unveränderlich) und enthalten in der Regel eine heterogene Folge von Elementen, auf die durch Entpacken (siehe später in diesem Abschnitt) oder Indizierung (oder sogar durch Attribute im Fall von namedtuples) zugegriffen wird. Listen sind "mutable" (=veränderlich), und ihre Elemente sind normalerweise homogen und werden durch Iteration über die Liste angesprochen.
Ein spezielles Problem ist die Konstruktion von Tupeln, die 0 oder 1 Elemente enthalten: Die Syntax hat einige zusätzliche Eigenheiten, um diese zu berücksichtigen. Leere Tupel werden durch ein leeres Klammerpaar konstruiert. Ein Tupel mit einem Element wird konstruiert, indem einem Wert ein Komma folgt - es genügt nicht, einen einzelnen Wert in Klammern zu setzen! Hässlich, aber effektiv. Zum Beispiel:
>>> empty = ()
>>> singleton = 'hello', # <-- note trailing comma
len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)
Die Anweisung t = 12345, 54321, 'hallo!'
ist ein Beispiel für tuple packing: die Werte 12345
, 54321
und 'hallo!'
werden zu einem Tupel verpackt. Die umgekehrte Operation ist ebenfalls möglich:
>>> x, y, z = t
Dies wird passenderweise sequnce unpacking (auf Deutsch: Entpacken einer Sequenz) genannt und funktioniert für jede beliebige Sequenz auf der rechten Seite. Das Entpacken von Sequenzen setzt voraus, dass sich auf der linken Seite des Gleichheitszeichens so viele Variablen befinden, wie es Elemente in der Sequenz gibt. Zu beachten ist, dass die Mehrfachzuweisung eigentlich nur eine Kombination aus Tupel-Packing und Sequence-Unpacking ist.
Sets
Python enthält auch einen Datentyp für Sets (auf Deutsch: Menge). Ein Set ist eine ungeordnete Sammlung, die keine doppelten Elemente enthält. Zu den grundlegenden Verwendungszwecken gehören Zugehörigkeitstests und das Eliminieren doppelter Einträge. Sets unterstützen auch mathematische Operationen wie Vereinigung, Schnittmenge, Differenz und symmetrische Differenz.
Geschweifte Klammern oder die Funktion set können verwendet werden, um Mengen zu erzeugen.
Hinweis: Um eine leere Menge zu erzeugen, muss set()
verwendet werden, nicht {}
! Letzteres erzeugt ein leeres Wörterbuch, eine Datenstruktur, die im nächsten Abschnitt behandelt wird.
Eine kurze Demonstration zu Sets:
>>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket) # doppelt Einträge wurden automatisch entfernt
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket # schneller Test auf Zugehörigkeit
True
>>> 'crabgrass' in basket
False
>>> # Set Operationen für einzigartige Buchstaben von zwei Worten
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a # einzigartige Buchstaben in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b # Buchstaben in a aber nicht in b
{'r', 'd', 'b'}
>>> a | b # Buchstaben in a oder b oder beiden
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b # Buchstaben in a und b
{'a', 'c'}
>>> a ^ b # Buchstaben in a oder b aber nicht in beiden
{'r', 'd', 'b', 'm', 'z', 'l'}
Analog zu List Comprehensions gibt es auch Set Comprehensions:
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}
Dictionaries - Wörterbücher
Ein weiterer nützlicher Datentyp, der in Python eingebaut und sehr oft verwendet wird, ist das Dictionary (auf Deutsch: Wörterbuch). Wörterbücher sind vom Typ mapping. Wörterbücher sind gegenwärtig die einzige Datenstruktur vom Typ mapping. Für Wörterbücher ist als Bezeichnung auch der Begriff "dict" (Kurzform für Dictionary) gebräuchlich.
Wörterbücher sind in anderen Sprachen manchmal als "assoziative Speicher" oder "assoziative Arrays" bekannt. Im Gegensatz zu Sequenzen, die durch einen Zahlenbereich indiziert werden, werden Wörterbücher durch Schlüssel indiziert, die jeder unveränderliche Typ sein können. Strings und Zahlen können ein Schlüssel sein. Tupel können als Schlüssel verwendet werden, wenn sie nur Strings, Zahlen oder Tupel enthalten. Wenn ein Tupel direkt oder indirekt ein veränderbares Objekt enthält, kann es nicht als Schlüssel verwendet werden. Listen können nicht als Schlüssel verwendet werden, da Listen mit Indexzuweisungen, Slice-Zuweisungen oder Methoden list.append
und list.extend
an Ort und Stelle verändert werden können.
Am besten stellt man sich ein Wörterbuch als eine Menge von Schlüssel-Wert-Paaren vor, wobei die Schlüssel innerhalb eines Wörterbuchs eindeutig sein müssen. Ein Paar geschweifte Klammern erzeugt ein leeres Wörterbuch: {}
. Eine kommagetrennte Liste von Schlüssel-Wert Paaren innerhalb der geschweiften Klammern fügt dem Wörterbuch erste Schlüssel-Wert Paare hinzu. Dies ist auch die Art und Weise, wie Wörterbücher bei der Ausgabe geschrieben werden.
Die wichtigsten Operationen mit einem Wörterbuch sind das Speichern eines Wertes mit einem Schlüssel und das Extrahieren des Wertes mithilfe des Schlüssels. Es ist auch möglich, ein Schlüssel-Wert-Paar mit del
zu löschen. Wenn man einen Schlüssel speichert, der bereits in Gebrauch ist, wird der alte Wert, der mit diesem Schlüssel verbunden war, überschrieben. Greift man auf einen Schlüssel zu, der nicht existiert, erhält man einen KeyError
.
Wenn man list(d)
auf ein Wörterbuch anwendet, erhält man eine Liste aller Schlüssel, die in dem Wörterbuch verwendet werden - und zwar in der Reihenfolge, in der die Schlüssel hinzugefügt wurden. Wenn man die SChlüssel aplhapbetisch sortiert haben will, verwendet man stattdessen sorted(d)
. Um zu prüfen, ob ein einzelner Schlüssel im Wörterbuch enthalten ist, verwendet man das Schlüsselwort in
.
Im Folgenden ein kleines Beispiel für die Nutzung als Wörterbuch:
>>> tel = {'jack': 4098, 'sape': 4139} # Anlegen von tel mit zwei Schlüssel-Werte Paaren
>>> tel['guido'] = 4127 # Hinzufügen eines Werten Paars
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack'] # Abfrage des Schlüssels "jack", der Rückgabewert ist der zugehörige Wert
4098
>>> del tel['sape'] # Entfernen des Schlüssels "sape" und damit auch des zugehörigen Werts
>>> tel['irv'] = 4127 # Hinzufügen des SChlüssel "irv"
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv'] # Ausgabe aller Schlüssel als Liste
>>> sorted(tel) # Sortieren des Dicts nach Schlüsseln
['guido', 'irv', 'jack']
>>> 'guido' in tel # Prüfen, ob der Schlüssel "guido" enthalten ist
True
>>> 'jack' not in tel # Prüfen, ob der Schlüssel "jack" nicht enthalten ist
False
Der dict Konstruktor erstellt Wörterbücher direkt aus Sequenzen von Schlüssel-Werte-Paaren:
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}
Darüber hinaus gibt es auch Dict Comprehensions, um Wörterbücher aus beliebigen Schlüssel- und Wertausdrücken zu erstellen. Das Ganze funktioniert analog zu List Comprehensions, nur das man bei Dict Comprehensions Schlüssel-Wert Paare schreibt. Dict Comprehensions werden ebenfalls in geschweifte Klammer eingeschlossen.
Das folgende Beispiel erzeigt ein Wörterbuch, wo der Schlüssel eine Ganzzahl ist und der zugehörige Wert dessen Quadrat:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
Wenn die Schlüssel einfache Zeichenketten sind, ist es manchmal einfacher, Paare mit Schlüsselwortargumenten anzugeben:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}
weitere Methoden von Wörterbüchern
Iteriert man über ein Wörterbuch erhält man die Schlüssel als Rückgabewert. Um Schlüssel und Wert zu erhalten nutzt man die dict.items()
Methode:
>>> tel = {'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> for x in tel:
... print(x)
...
jack
sape
guido
>>> for key, value in tel.items():
... print(key, value)
...
jack 4098
sape 4139
guido 4127
Die Methode dict.len()
gibt die Länge des Wörterbuchs zurück, also die Anzahl der Einträge. Versucht man, auf einem Schlüssel zuzugreifen, der nicht existiert, bekommt man wie oben bereits erwähnt einen KeyError
. Wenn man stattdessen einen Vorgabewert zurückerhalten möchte nutzt man der dict.get(Schlüssel[, Defaultwert)
Methode:
>>> tel['peter']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'peter'
>>> tel.get('peter', 0)
0
>>> tel.get('jack', 0)
4098
Gibt man bei dict.get()
keinen Defaultwert an erhält man stattdessen None
als Rückgabewert.
Wörterbücher kennen noch eine ganze Reihe weiterer Optionen, welche in der Dokumentation nachgelesen werden können.
mehr zur Iteration über Datenstrukturen
Das Iterieren über Schlüssel-Wert Paare eines Wörterbuchs wurde bereits im vorherigen Abschnitt gezeigt.
Beim Durchlaufen einer Schleife durch eine Sequenz können der Positionsindex und der entsprechende Wert gleichzeitig mit der Funktion enumerate
abgefragt werden. Das funktioniert mit jeder Sequenz, über die iteriert werden kann, also auch Tupeln, Sets und Wörterbüchern:
>>> for index, item in enumerate(['tic', 'tac', 'toe']):
... print(index, item)
...
0 tic
1 tac
2 toe
Um zwei oder mehr Sequenzen gleichzeitig zu durchlaufen, können die Einträge mit der Funktion zip zusammengezogen werden:
>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for question, answer in zip(questions, answers):
... print('What is your {0}? It is {1}.'.format(question, answer))
...
What is your name? It is lancelot.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.
Um eine Sequenz in umgekehrter Reihenfolge zu durchlaufen, ruft man die Sequenz mit der reversed Funktion auf:
>>> for i in reversed(range(1, 10, 2)):
... print(i)
...
9
7
5
3
1
Um eine Sequenz in sortierter Reihenfolge in einer Schleife zu durchlaufen, verwendet man die Funktion sorted, die eine neue sortierte Liste zurückgibt, während die Sequenz an sich unverändert bleibt:
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for i in sorted(basket):
... print(i)
...
apple
apple
banana
orange
orange
pear
Die Verwendung von set
auf eine Sequenz eliminiert doppelte Elemente. Die Verwendung von sorted
in Kombination mit set
über einer Sequenz ist ein idiomatischer Weg, eine Schleife über eindeutige Elemente der Sequenz in sortierter Reihenfolge zu bringen:
>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
... print(f)
...
apple
banana
orange
pear
Manchmal ist es verlockend, eine Liste zu ändern, während man sie in einer Schleife durchläuft. Es ist jedoch oft einfacher und vor allem sicherer, stattdessen eine neue Liste zu erstellen:
>>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
... if not math.isnan(value):
... filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]
mehr über Bedingungen für if und while
Die in while
und if
Anweisungen verwendeten Bedingungen können beliebige Operatoren enthalten, nicht nur einfache Vergleiche.
Die Vergleichsoperatoren in
und not in
sind Zugehörigkeitstests, die feststellen, ob ein Wert in einem Container wie z.B. einer Sequenz oder einem Wörterbuch ist (oder nicht). Die Operatoren is
und is not
vergleichen, ob zwei Objekte wirklich dasselbe Objekt ist. Alle Vergleichsoperatoren haben die gleiche Priorität, die niedriger ist als die aller numerischen Operatoren.
Vergleiche können verkettet werden. Zum Beispiel testet a < b == c
, ob a
kleiner als b
ist und außerdem b
gleich c
ist.
Vergleiche können mit den booleschen Operatoren and
und or
kombiniert werden, und das Ergebnis eines Vergleichs (oder jedes anderen booleschen Ausdrucks) kann mit not
negiert werden. Diese haben eine niedrigere Priorität als Vergleichsoperatoren. Zwischen ihnen hat not
die höchste Priorität und or
die niedrigste, sodass A and not B or C
äquivalent zu (A and (not B)) or C
ist. Wie immer können Klammern verwendet werden, um die gewünschte Zusammensetzung auszudrücken.
Beispiel dazu: Im Folgenden Bespiel spielt der Wert von C
für den Vergleich beim if
keine Rolle, weil bereits A and not B
wahr ist und dadurch das ... or C
nicht mehr relevant ist:
>>> A = 'irgendwas'
>>> B = None
>>> C = None
>>> if A and not B or C:
... print('ist wahr')
...
ist wahr
>>> C = 'auch was'
>>> if A and not B or C:
... print('ist wahr')
...
ist wahr
Die booleschen Operatoren and
und or
sind sogenannte Kurzschluss-Operatoren: Ihre Argumente werden von links nach rechts ausgewertet, und die Auswertung endet, sobald das Ergebnis feststeht. Wenn zum Beispiel A
und C
wahr sind, aber B
falsch ist, wertet A and B and C
den Ausdruck C
nicht aus. Wenn er als allgemeiner Wert und nicht als Boolescher Wert verwendet wird, ist der Rückgabewert eines Kurzschlussoperators das zuletzt ausgewertete Argument.
Es ist möglich, das Ergebnis eines Vergleichs oder eines anderen booleschen Ausdrucks einer Variablen zuzuweisen. Zum Beispiel:
>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'
Zu beachten ist, dass in Python, anders als in C, Zuweisungen innerhalb von Ausdrücken explizit mit dem walrus Operator :=
erfolgen müssen. Dies vermeidet eine häufige Form von Problemen, die in C-Programmen auftreten: die Eingabe von =
in einem Ausdruck, obwohl ==
beabsichtigt war.
Vergleiche von Sequenzen und anderen Datentypen
Sequenzobjekte können normalerweise mit anderen Objekten desselben Sequenztyps verglichen werden. Der Vergleich verwendet eine lexikografische Reihenfolge: zuerst werden die ersten beiden Elemente verglichen, und wenn sie sich unterscheiden, bestimmt dies das Ergebnis des Vergleichs. Wenn sie gleich sind, werden die nächsten beiden Elemente verglichen, und so weiter, bis eine der beiden Sequenzen erschöpft ist. Sind zwei zu vergleichende Elemente selbst Sequenzen desselben Typs, wird der lexikografische Vergleich rekursiv durchgeführt. Wenn alle Elemente zweier Sequenzen gleich sind, werden die Sequenzen als gleich betrachtet. Ist eine Sequenz eine anfängliche Untersequenz der anderen, so ist die kürzere Sequenz die kleinere (geringere). Die lexikografische Ordnung für Zeichenketten verwendet die Unicode-Codepunktnummer, um die einzelnen Zeichen zu ordnen. Einige Beispiele für Vergleiche zwischen Sequenzen desselben Typs:
(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4) < (1, 2, 4)
(1, 2) < (1, 2, -1)
(1, 2, 3) == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab')) < (1, 2, ('abc', 'a'), 4)
Zu beachten ist, dass der Vergleich von Objekten unterschiedlichen Typs mit <
oder >
zulässig ist, sofern die Objekte über geeignete Vergleichsmethoden verfügen. Zum Beispiel werden gemischte numerische Typen entsprechend ihrem numerischen Wert verglichen, sodass 0 gleich 0.0 ist, usw. Andernfalls wird der Interpreter eine TypeError
Ausnahme auslösen, anstatt eine beliebige Reihenfolge zu liefern.
- nächstes Kapitel: Module
- vorheriges Kapitel: weitere Werkzeuge zur Steuerung des Programmflusses