5. Datenstrukturen
******************

In diesem Kapitel werden einige Dinge, die Sie bereits kennengelernt
haben, näher erläutert, und es werden zudem einige neue Aspekte
hinzugefügt.


5.1. Mehr zum Thema Listen
==========================

Der Datentyp list verfügt über einige weitere Methoden. Hier sind alle
Methoden von Listenobjekten aufgeführt:

list.append(value, /)

   Füge einen Eintrag am Ende der Liste hinzu. Ähnlich wie bei
   "a[len(a):] = [x]".

list.extend(iterable, /)

   Erweitere die Liste, indem du alle Elemente aus der iterierbaren
   Struktur anhängst. Ähnlich wie bei "a[len(a):] = iterable".

list.insert(index, value, /)

   Fügt ein Element an einer bestimmten Position ein. Das erste
   Argument ist der Index des Elements, vor dem eingefügt werden soll.
   Daher fügt "a.insert(0, x)" am Anfang der Liste ein, und
   "a.insert(len(a), x)" entspricht "a.append(x)".

list.remove(value, /)

   Entferne das erste Element aus der Liste, dessen Wert *value*
   entspricht. Wenn kein solches Element vorhanden ist, wird eine
   "ValueError" ausgelöst.

list.pop(index=-1, /)

   Entfernt das Element an der angegebenen Position aus der Liste und
   gibt es zurück. Wenn kein Index angegeben wird, entfernt "a.pop()"
   das letzte Element der Liste und gibt es zurück. Es löst eine
   "IndexError" aus, wenn die Liste leer ist oder der Index außerhalb
   des Listenbereichs liegt.

list.clear()

   Entferne alle Elemente aus der Liste. Ähnlich wie bei "del a[:]" .

list.index(value[, start[, stop]])

   Gibt den nullbasierten Index des ersten Vorkommens von *value* in
   der Liste zurück. Löst eine Ausnahme vom Typ "ValueError" aus, wenn
   kein entsprechendes Element vorhanden ist.

   Die optionalen Argumente *start* und *end* werden wie in der Slice-
   Notation interpretiert und dienen dazu, die Suche auf eine
   bestimmte Teilfolge der Liste zu beschränken. Der zurückgegebene
   Index wird relativ zum Anfang der vollständigen Folge und nicht
   relativ zum Argument *start* berechnet.

list.count(value, /)

   Gibt die Anzahl der Vorkommen von *value* in der Liste zurück.

list.sort(*, key=None, reverse=False)

   Sortiere die Elemente der Liste an Ort und Stelle (die Argumente
   können zur Anpassung der Sortierung verwendet werden; eine
   Erläuterung findest du unter "sorted()" ).

list.reverse()

   Die Elemente der Liste an Ort und Stelle in umgekehrter Reihenfolge
   anordnen.

list.copy()

   Gibt eine flache Kopie der Liste zurück. Ähnlich wie bei "a[:]" .

Ein Beispiel, das die meisten Methoden der Liste verwendet:

   >>> fruits = ['Orange', 'Apfel', 'Birne', 'Banane', 'Kiwi', 'Apfel', 'Banane']
   >>> fruits.count('Apfel')
   2
   >>> fruits.count('Mandarine')
   0
   >>> fruits.index('Banane')
   3
   >>> fruits.index('banana', 4)  # Die nächste Banane ab Position 4 finden
   6
   >>> fruits.reverse()
   >>> fruits
   ['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
   >>> fruits.append('traube')
   >>> fruits
   ['banane', 'apfel', 'kiwi', 'banane', 'birne', 'apfel', 'orange', 'traube']
   >>> fruits.sort()
   >>> fruits
   ['Apfel', 'Apfel', 'Banane', 'Banane', 'Traube', 'Kiwi', 'Orange', 'Birne']
   >>> fruits.pop()
   'Birne'

Vielleicht ist Ihnen aufgefallen, dass bei Methoden wie "insert",
"remove" oder "sort", die lediglich die Liste verändern, kein
Rückgabewert ausgegeben wird – sie geben den Standardwert "None"
zurück. [1] Dies ist ein Entwurfsprinzip für alle veränderbaren
Datenstrukturen in Python.

Außerdem wird Ihnen vielleicht auffallen, dass nicht alle Daten
sortiert oder verglichen werden können. So lässt sich beispielsweise
"[None, 'hello', 10]" nicht sortieren, da Ganzzahlen nicht mit
Zeichenketten verglichen werden können und "None" nicht mit anderen
Typen verglichen werden kann. Zudem gibt es einige Typen, für die
keine definierte Ordnungsrelation existiert. So ist beispielsweise
"3+4j < 5+7j" kein gültiger Vergleich.


5.1.1. Listen als Stapel verwenden
----------------------------------

Die Listenmethoden machen es sehr einfach, eine Liste als Stapel zu
verwenden, bei dem das zuletzt hinzugefügte Element als erstes wieder
abgerufen wird ("Last-in, First-out").  Um ein Element oben auf den
Stapel zu setzen, verwenden Sie "append()".  Um ein Element von der
Spitze des Stapels abzurufen, verwenden Sie "pop()" ohne expliziten
Index.  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]


5.1.2. Listen als Warteschlangen verwenden
------------------------------------------

It is also possible to use a list as a queue, where the first element
added is the first element retrieved ("first-in, first-out"); however,
lists are not efficient for this purpose.  While appends and pops from
the end of list are fast, doing inserts or pops from the beginning of
a list is slow (because all of the other elements have to be shifted
by one).

Um eine Warteschlange zu implementieren, verwenden Sie
"collections.deque" “, das für schnelles Hinzufügen und Entnehmen von
beiden Enden ausgelegt ist.  Beispiel:

   >>> from collections import deque
   >>> queue = deque(["Eric", "John", "Michael"])
   >>> queue.append("Terry")           # Terry kommt an
   >>> queue.append("Graham")          # Graham kommt an
   >>> queue.popleft()                 # Der Erste, der angekommen ist, verlässt nun die Warteschlange
   'Eric'
   >>> queue.popleft()                 # Der Zweite, der angekommen ist, verlässt nun die Warteschlange
   'John'
   >>> queue                           # Verbleibende Warteschlange in der Reihenfolge der Ankunft
   deque(['Michael', 'Terry', 'Graham'])


5.1.3. Listen-Abstraktion
-------------------------

Listen-Abstraktion bieten eine prägnante Möglichkeit, Listen zu
erstellen. Häufige Anwendungsfälle sind die Erstellung neuer Listen,
bei denen jedes Element das Ergebnis bestimmter Operationen ist, die
auf jedes Element einer anderen Sequenz oder eines anderen
iterierbaren Objekts angewendet werden, oder die Erstellung einer
Teilsequenz aus den Elementen, die eine bestimmte Bedingung erfüllen.

Nehmen wir zum Beispiel an, wir möchten eine Liste von Quadraten
erstellen, etwa wie folgt:

   >>> squares = []
   >>> for x in range(10):
   ...     squares.append(x**2)
   ...
   >>> squares
   [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Beachten Sie, dass dadurch eine Variable namens "x" angelegt (oder
überschrieben) wird, die auch nach Abschluss der Schleife noch
vorhanden ist. Wir können die Liste der Quadrate ohne Nebenwirkungen
wie folgt berechnen:

   Quadrate = list(map(lambda x: x**2, range(10)))

oder, gleichbedeutend:

   Quadrate = [x**2 for x in range(10)]

was prägnanter und besser lesbar ist.

Eine Listenkomprimierung besteht aus Klammern, die einen Ausdruck
enthalten, gefolgt von einer "for" “-Klausel und anschließend null
oder mehr "for" “- oder "if" “-Klauseln. Das Ergebnis ist eine neue
Liste, die sich aus der Auswertung des Ausdrucks im Kontext der darauf
folgenden "for" “- und "if" “-Klauseln ergibt. Diese
Listenkomprimierung kombiniert beispielsweise die Elemente zweier
Listen, sofern sie nicht identisch sind:

   >>> [(x, y) für x in [1, 2, 3] für y in [3, 1, 4], wenn x != y]
   [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

und das entspricht:

   >>> 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)]

Beachten Sie, dass die Reihenfolge der Anweisungen "for" und "if" in
beiden Codeausschnitten identisch ist.

Wenn es sich bei dem Ausdruck um ein Tupel handelt (z. B. "(x, y)" im
vorherigen Beispiel), muss es in Klammern gesetzt werden.

   >>> vec = [-4, -2, 0, 2, 4]
   >>> # Erstelle eine neue Liste, in der die Werte verdoppelt sind
   >>> [x*2 for x in vec]
   [-8, -4, 0, 4, 8]
   >>> # Filtere die Liste, um negative Zahlen auszuschließen
   >>> [x for x in vec if x >= 0]
   [0, 2, 4]
   >>> # Eine Funktion auf alle Elemente anwenden
   >>> [abs(x) for x in vec]
   [4, 2, 0, 2, 4]
   >>> # Eine Methode auf jedes Element anwenden
   >>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
   >>> [weapon.strip() for weapon in freshfruit]
   ['banana', 'loganberry', 'passion fruit']
   >>> # Eine Liste von 2-Tupeln wie (Zahl, Quadrat) erstellen
   >>> [(x, x**2) for x in range(6)]
   [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
   >>> # Das Tupel muss in Klammern gesetzt werden, sonst wird ein Fehler ausgelöst
   >>> [x, x**2 for x in range(6)]
     Datei "<stdin>", Zeile 1
       [x, x**2 for x in range(6)]
        ^^^^^^^
   SyntaxError: Hast du die Klammern um das Ziel der List Comprehension vergessen?
   >>> # Eine Liste mithilfe einer List Comprehension mit zwei "for"-Schleifen abflachen
   >>> 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]

Listen-Abstraktionen 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']


5.1.4. Verschachtelte Listen-Abstraktion
----------------------------------------

Der erste Ausdruck in einer Listen-Abstraktion kann ein beliebiger
Ausdruck sein, einschließlich einer weiteren Listen-Abstraktion.

Betrachte das folgende Beispiel einer 3×4-Matrix, die als Liste aus
drei Listen der Länge 4 implementiert ist:

   >>> matrix = [
   ...     [1, 2, 3, 4],
   ...     [5, 6, 7, 8],
   ...     [9, 10, 11, 12],
   ... ]

Die folgende Listen-Abstraktion transponiert Zeilen und Spalten:

   >>> [[row[i] for row in matrix] for i in range(4)]
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

Wie wir im vorigen Abschnitt gesehen haben, wird die innere
Listenauswertung im Kontext des darauf folgenden "for" ausgewertet,
sodass dieses Beispiel gleichbedeutend ist mit:

   >>> 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 dasselbe ist wie:

   >>> transposed = []
   >>> for i in range(4):
   ...     # Die folgenden 3 Zeilen implementieren die verschachtelte Listenkomposition
   ...     transposed_row = []
   ...     for row in matrix:
   ...         transposed_row.append(row[i])
   ...     transponiert.append(transponierte_Zeile)
   ...
   >>> transponiert
   [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

In der Praxis sollten Sie integrierte Funktionen komplexen
Ablaufanweisungen vorziehen. Die Funktion "zip()" eignet sich
hervorragend für diesen Anwendungsfall:

   >>> list(zip(*matrix))
   [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

Weitere Informationen zum Sternchen in dieser Zeile finden Sie unter
Entpacken von Argumentlisten.


5.2. Die Erklärung der "del" “
==============================

Es gibt eine Möglichkeit, ein Element anhand seines Indexes statt
seines Wertes aus einer Liste zu entfernen: die Anweisung "del" “.
Diese unterscheidet sich von der Methode "pop()" “, die einen Wert
zurückgibt. Die Anweisung "del" kann auch verwendet werden, um
Teilmengen aus einer Liste zu entfernen oder die gesamte Liste zu
leeren (was wir zuvor durch die Zuweisung einer leeren Liste zur
Teilmenge getan haben). 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 zum Löschen ganzer Variablen verwendet werden:

   >>> del a

Der Verweis auf den Namen "a" ist hier ein Fehler (zumindest solange,
bis ihm ein anderer Wert zugewiesen wird). Wir werden später noch
weitere Verwendungsmöglichkeiten für "del" finden.


5.3. Tupel und Sequenzen
========================

Wir haben gesehen, dass Listen und Zeichenketten viele gemeinsame
Eigenschaften haben, wie beispielsweise Indizierungs- und
Ausschnittoperationen. Sie sind zwei Beispiele für
*Sequenz*-Datentypen (siehe Sequenztypen – list, tuple, range). Da
Python eine sich weiterentwickelnde Sprache ist, können weitere
Sequenz-Datentypen hinzukommen. Es gibt außerdem einen weiteren
standardmäßigen Sequenz-Datentyp: das *Tupel*.

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!')
   >>> # Tupel können verschachtelt sein:
   >>> u = t, (1, 2, 3, 4, 5)
   >>> u
   ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
   >>> # Tupel sind unveränderlich:
   >>> t[0] = 88888
   Traceback (letzter Aufruf zuletzt):
     Datei "<stdin>", Zeile 1, in <module>
   TypeError: Das Objekt "tuple" unterstützt keine Elementzuweisung
   >>> # können jedoch veränderbare Objekte enthalten:
   >>> v = ([1, 2, 3], [3, 2, 1])
   >>> v
   ([1, 2, 3], [3, 2, 1])

Wie Sie sehen, werden Tupel in der Ausgabe immer in Klammern gesetzt,
damit verschachtelte Tupel korrekt interpretiert werden; sie können
mit oder ohne umschließende Klammern eingegeben werden, obwohl
Klammern oft ohnehin erforderlich sind (wenn das Tupel Teil eines
größeren Ausdrucks ist).  Es ist nicht möglich, den einzelnen
Elementen eines Tupels Werte zuzuweisen; es ist jedoch möglich, Tupel
zu erstellen, die veränderbare Objekte wie beispielsweise Listen
enthalten.

Auch wenn Tupel auf den ersten Blick Listen ähneln mögen, werden sie
oft in anderen Situationen und für andere Zwecke verwendet. Tupel sind
*unveränderlich* und enthalten in der Regel eine heterogene Folge von
Elementen, auf die über Entpacken (siehe weiter unten in diesem
Abschnitt) oder Indizierung (oder im Fall von "namedtuples" sogar über
Attribute) zugegriffen wird. Listen sind *veränderlich*, und ihre
Elemente sind in der Regel homogen; der Zugriff erfolgt durch
Iteration über die Liste.

Ein besonderes Problem stellt die Bildung von Tupeln dar, die 0 oder 1
Element enthalten: Die Syntax weist hierfür einige zusätzliche
Besonderheiten auf.  Leere Tupel werden durch ein leeres Klammerpaar
gebildet; ein Tupel mit einem Element wird gebildet, indem man einem
Wert ein Komma nachsetzt (es reicht nicht aus, einen einzelnen Wert in
Klammern zu setzen). Unschön, aber effektiv.  Zum Beispiel:

   >>> empty = ()
   >>> singleton = 'hello',    # <-- note trailing comma
   >>> len(empty)
   0
   >>> len(singleton)
   1
   >>> singleton
   ('hello',)

Die Anweisung "t = 12345, 54321, 'hello!'" ist ein Beispiel für
*Tupel-Packing*: Die Werte "12345" “, "54321" und "'hello!'" “ werden
in einem Tupel zusammengefasst. Auch die umgekehrte Operation ist
möglich:

   >>> x, y, z = t

Dies wird passenderweise als *Sequenz-Entpackung* bezeichnet und
funktioniert für jede beliebige Sequenz auf der rechten Seite. Für die
Sequenz-Entpackung muss die Anzahl der Variablen auf der linken Seite
des Gleichheitszeichens der Anzahl der Elemente in der Sequenz
entsprechen.  Beachten Sie, dass die Mehrfachzuweisung im Grunde nur
eine Kombination aus Tupel-Packing und Sequenz-Unpacking ist.


5.4. Sets
=========

Python enthält außerdem einen Datentyp für Mengen. Eine Menge ist eine
ungeordnete Sammlung ohne doppelte Elemente. Zu den grundlegenden
Anwendungsbereichen gehören die Überprüfung der Zugehörigkeit zu einer
Menge und das Entfernen doppelter Einträge. Mengenobjekte unterstützen
zudem mathematische Operationen wie Vereinigung, Schnittmenge,
Differenz und symmetrische Differenz.

Zum Erstellen von Mengen können geschweifte Klammern oder die Funktion
"set()" verwendet werden. Hinweis: Um eine leere Menge zu erstellen,
müssen Sie "set()" verwenden, nicht "{}" “; Letzteres erstellt ein
leeres Wörterbuch, eine Datenstruktur, die wir im nächsten Abschnitt
behandeln.

Da Mengen ungeordnet sind, kann es beim Durchlaufen oder Ausgeben der
Elemente vorkommen, dass diese in einer anderen Reihenfolge angezeigt
werden, als Sie erwarten.

Hier eine kurze Demonstration:

   >>> basket = {'Apfel', 'Orange', 'Apfel', 'Birne', 'Orange', 'Banane'}
   >>> print(basket)                      # zeigt, dass Duplikate entfernt wurden
   {'Orange', 'Banane', 'Birne', 'Apfel'}
   >>> 'orange' in basket                 # schnelle Zugehörigkeitsprüfung
   True
   >>> 'crabgrass' in basket
   False

   >>> # Demonstration von Mengenoperationen mit eindeutigen Buchstaben aus zwei Wörtern
   >>>
   >>> a = set('abracadabra')
   >>> b = set('alacazam')
   >>> a                                  # eindeutige Buchstaben in a
   {'a', 'r', 'b', 'c', 'd'}
   >>> a - b                              # Buchstaben in a, die nicht in b vorkommen
   {'r', 'd', 'b'}
   >>> a | b                              # Buchstaben in a oder b oder in beiden
   {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
   >>> a & b                              # Buchstaben, die sowohl in a als auch in b vorkommen
   {'a', 'c'}
   >>> a ^ b                              # Buchstaben, die entweder in a oder in b vorkommen, aber nicht in beiden
   {'r', 'd', 'b', 'm', 'z', 'l'}

Ähnlich wie bei den Listenkomprimierungen werden auch
Mengekomprimierungen unterstützt:

   >>> a = {x for x in 'abracadabra' if x not in 'abc'}
   >>> a
   {'r', 'd'}


5.5. Wörterbücher
=================

Ein weiterer nützlicher, in Python integrierter Datentyp ist das
*Wörterbuch* (siehe Mapping-Typen – dict). Wörterbücher werden in
anderen Sprachen manchmal als "assoziative Speicher" oder "assoziative
Arrays" bezeichnet.  Im Gegensatz zu Sequenzen, die durch einen
Zahlenbereich indiziert werden, werden Dictionaries durch *Schlüssel*
indiziert, die von jedem unveränderlichen Typ sein können;
Zeichenketten und Zahlen können immer als Schlüssel verwendet werden.
Tupel können als Schlüssel verwendet werden, wenn sie ausschließlich
Zeichenketten, Zahlen oder Tupel enthalten; enthält ein Tupel direkt
oder indirekt ein veränderbliches Objekt, kann es nicht als Schlüssel
verwendet werden. Listen können nicht als Schlüssel verwendet werden,
da sie durch Indexzuweisungen, Slice-Zuweisungen oder Methoden wie
"append()" und "extend()" an Ort und Stelle verändert werden können.

Man kann sich ein Wörterbuch am besten als eine Menge von
*Schlüssel:Wert*-Paaren vorstellen, wobei die Schlüssel (innerhalb
eines Wörterbuchs) eindeutig sein müssen. Ein Paar geschweifter
Klammern erstellt ein leeres Wörterbuch: "{}". Durch Einfügen einer
durch Kommas getrennten Liste von Schlüssel:Wert-Paaren innerhalb der
geschweiften Klammern werden dem Wörterbuch anfängliche Schlüssel
:Wert-Paare hinzugefügt; auf diese Weise werden Wörterbücher auch bei
der Ausgabe dargestellt.

Die wichtigsten Operationen in einem Wörterbuch sind das Speichern
eines Werts unter einem bestimmten Schlüssel und das Abrufen des Werts
anhand des Schlüssels. Es ist außerdem möglich, ein Schlüssel-Wert-
Paar mit ` "del`" zu löschen. Wenn Sie einen Wert unter einem
Schlüssel speichern, der bereits verwendet wird, wird der alte, diesem
Schlüssel zugeordnete Wert verworfen.

Der Versuch, einen Wert für einen nicht vorhandenen Schlüssel mittels
Indexierung abzurufen ("d[key]"), löst einen "KeyError" aus. Um diesen
Fehler beim Zugriff auf einen möglicherweise nicht vorhandenen
Schlüssel zu vermeiden, verwenden Sie stattdessen die Methode "get()",
die "None" (oder einen angegebenen Standardwert) zurückgibt, wenn der
Schlüssel nicht im Wörterbuch enthalten ist.

Wendet man "list(d)" auf ein Wörterbuch an, wird eine Liste aller im
Wörterbuch verwendeten Schlüssel in der Reihenfolge ihrer Einfügung
zurückgegeben (wenn Sie die Liste sortiert haben möchten, verwenden
Sie stattdessen einfach "sorted(d)" “). Um zu prüfen, ob ein einzelner
Schlüssel im Wörterbuch enthalten ist, verwenden Sie das Schlüsselwort
"in" .

Hier ist ein kleines Beispiel, bei dem ein Wörterbuch verwendet wird:

   >>> tel = {'jack': 4098, 'sape': 4139}
   >>> tel['guido'] = 4127
   >>> tel
   {'jack': 4098, 'sape': 4139, 'guido': 4127}
   >>> tel['jack']
   4098
   >>> tel['irv']
   Traceback (letzter Aufruf zuletzt):
     Datei "<stdin>", Zeile 1, in <module>
   KeyError: 'irv'
   >>> print(tel.get('irv'))
   None
   >>> del tel['sape']
   >>> tel['irv'] = 4127
   >>> tel
   {'jack': 4098, 'guido': 4127, 'irv': 4127}
   >>> list(tel)
   ['jack', 'guido', 'irv']
   >>> sorted(tel)
   ['guido', 'irv', 'jack']
   >>> 'guido' in tel
   True
   >>> 'jack' not in tel
   False

Der Konstruktor ` "dict()" ` erstellt Wörterbücher direkt aus
Sequenzen von Schlüssel-Wert-Paaren:

   >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
   {'sape': 4139, 'guido': 4127, 'jack': 4098}

Darüber hinaus können Dict-Comprehensions verwendet werden, um
Wörterbücher aus beliebigen Schlüssel- und Wert-Ausdrücken zu
erstellen:

   >>> {x: x**2 for x in (2, 4, 6)}
   {2: 4, 4: 16, 6: 36}

Wenn es sich bei den Schlüsseln um einfache Zeichenfolgen handelt, ist
es manchmal einfacher, Paare mithilfe von Schlüsselwortargumenten
anzugeben:

   >>> dict(sape=4139, guido=4127, jack=4098)
   {'sape': 4139, 'guido': 4127, 'jack': 4098}


5.6. Schleifentechniken
=======================

Beim Durchlaufen von Wörterbüchern können der Schlüssel und der
zugehörige Wert mithilfe der Methode "items()" gleichzeitig abgerufen
werden.

   >>> knights = {'gallahad': 'der Reine', 'robin': 'der Tapfere'}
   >>> for k, v in knights.items():
   ...     print(k, v)
   ...
   gallahad der Reine
   robin der Tapfere

Beim Durchlaufen einer Sequenz können der Positionsindex und der
zugehörige Wert mithilfe der Funktion "enumerate()" gleichzeitig
abgerufen werden.

   >>> for i, v in enumerate(['tic', 'tac', 'toe']):
   ...     print(i, v)
   ...
   0 tic
   1 tac
   2 toe

Um zwei oder mehr Sequenzen gleichzeitig zu durchlaufen, können die
Einträge mit der Funktion "zip()" gepaart werden.

   >>> Fragen = ['Name', 'Aufgabe', 'Lieblingsfarbe']
   >>> Antworten = ['Lancelot', 'der Heilige Gral', 'blau']
   >>> for q, a in zip(Fragen, Antworten):
   ...     print('Wie heißt du {0}?  Du heißt {1}.'.format(q, a))
   ...
   Wie heißt du?  Du heißt Lancelot.
   Was ist deine Aufgabe?  Es ist der Heilige Gral.
   Was ist deine Lieblingsfarbe?  Es ist Blau.

Um eine Folge in umgekehrter Reihenfolge zu durchlaufen, geben Sie
zunächst die Folge in Vorwärtsrichtung an und rufen Sie anschließend
die Funktion "reversed()" auf.

   >>> for i in reversed(range(1, 10, 2)):
   ...     print(i)
   ...
   9
   7
   5
   3
   1

Um eine Folge in sortierter Reihenfolge zu durchlaufen, verwenden Sie
die Funktion "sorted()" “, die eine neue sortierte Liste zurückgibt,
während die Quelle unverändert bleibt.

   >>> basket = ['Apfel', 'Orange', 'Apfel', 'Birne', 'Orange', 'Banane']
   >>> for i in sorted(basket):
   ...     print(i)
   ...
   Apfel
   Apfel
   Banane
   Orange
   Orange
   Birne

Die Anwendung von "set()" auf eine Sequenz entfernt doppelte Elemente.
Die Verwendung von "sorted()" in Kombination mit "set()" auf eine
Sequenz ist eine gängige Methode, um die eindeutigen Elemente der
Sequenz in sortierter Reihenfolge durchzugehen.

   >>> basket = ['Apfel', 'Orange', 'Apfel', 'Birne', 'Orange', 'Banane']
   >>> for f in sorted(set(basket)):
   ...     print(f)
   ...
   Apfel
   Banane
   Orange
   Birne

Manchmal ist es verlockend, eine Liste zu ändern, während man sie in
einer Schleife durchläuft; oft ist es jedoch einfacher und 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]


5.7. Mehr zu den Bedingungen
============================

Die in den Anweisungen "while" und "if" verwendeten Bedingungen können
beliebige Operatoren enthalten, nicht nur Vergleichsoperatoren.

Die Vergleichsoperatoren "in" und "not in" sind
Zugehörigkeitsprüfungen, die feststellen, ob ein Wert in einem
Container enthalten ist (oder nicht). Die Operatoren "is" und "is not"
vergleichen, ob zwei Objekte tatsächlich dasselbe Objekt sind. Alle
Vergleichsoperatoren haben dieselbe Priorität, die niedriger ist als
die aller numerischen Operatoren.

Vergleiche können verkettet werden. Beispielsweise prüft "a < b == c"
“, ob "a" kleiner ist als "b" und ob darüber hinaus "b" gleich "c"
ist.

Vergleiche können mithilfe der Booleschen Operatoren "and" und "or"
kombiniert werden, und das Ergebnis eines Vergleichs (oder eines
beliebigen anderen Booleschen Ausdrucks) kann mit "not" negiert
werden. Diese haben eine niedrigere Priorität als
Vergleichsoperatoren; unter ihnen hat "not" die höchste Priorität und
"or" die niedrigste, sodass "A and not B or C" gleichbedeutend ist mit
"(A and (not B)) or C" “. Wie immer können Klammern verwendet werden,
um die gewünschte Zusammensetzung auszudrücken.

Die Booleschen Operatoren "and" und "or" sind sogenannte
*Kurzschluss*-Operatoren: Ihre Argumente werden von links nach rechts
ausgewertet, und die Auswertung wird beendet, sobald das Ergebnis
feststeht.  Sind beispielsweise "A" und "C" wahr, "B" jedoch falsch,
so wertet "A and B and C" den Ausdruck "C" nicht aus.  Wird ein
Kurzschlussoperator als allgemeiner Wert und nicht als Boolescher Wert
verwendet, ist der Rückgabewert das zuletzt ausgewertete Argument.

Es ist möglich, das Ergebnis eines Vergleichs oder eines anderen
booleschen Ausdrucks einer Variablen zuzuweisen.  Beispiel:

   >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
   >>> non_null = string1 or string2 or string3
   >>> non_null
   'Trondheim'

Beachten Sie, dass in Python – anders als in C – Zuweisungen innerhalb
von Ausdrücken explizit mit dem Walross-Operator ":=" erfolgen müssen.
Dadurch wird eine häufige Problemklasse vermieden, die in C-Programmen
auftritt: die Eingabe von "=" in einem Ausdruck, obwohl eigentlich
"==" gemeint war.


5.8. Vergleich von Sequenzen und anderen Typen
==============================================

Sequenzobjekte können in der Regel mit anderen Objekten desselben
Sequenztyps verglichen werden. Der Vergleich erfolgt nach
*lexikografischer* Reihenfolge: Zunächst werden die ersten beiden
Elemente verglichen; unterscheiden sie sich, bestimmt dies das
Ergebnis des Vergleichs; sind sie gleich, 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. Sind alle Elemente zweier Sequenzen gleich, gelten die
Sequenzen als gleich. Ist eine Sequenz eine anfängliche Teilsequenz
der anderen, ist die kürzere Sequenz die kleinere (geringere).  Bei
der lexikografischen Sortierung von Zeichenketten wird die Unicode-
Codepunktnummer zur Sortierung einzelner Zeichen verwendet. 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)

Beachten Sie, dass der Vergleich von Objekten unterschiedlicher Typen
mit "<" oder ">" zulässig ist, sofern die Objekte über entsprechende
Vergleichsmethoden verfügen. Beispielsweise werden gemischte
numerische Typen anhand ihres numerischen Werts verglichen, sodass 0
gleich 0,0 ist usw. Andernfalls liefert der Interpreter keine
willkürliche Reihenfolge, sondern löst eine "TypeError" “-Ausnahme
aus.

-[ Fußnoten ]-

[1] Andere Sprachen geben möglicherweise das geänderte Objekt zurück,
    was eine Methodenverkettung ermöglicht, wie beispielsweise
    "d->insert("a")->remove("b")->sort();".
