Dictionaries

Einführung

Webster's Dictionary

In den vorigen Kapiteln haben wir die sequentiellen Datentypen wie Listen, Strings und Tupel eingeführt. Nun wollen wir eine weitere Kategorie von Datentypen genauer untersuchen, die "Mapping". In dieser Kategorie gibt es allerdings zur Zeit nur einen implementierten Typ, das Dictionary. Beim Dictionary handelt es sich um ein assoziatives Feld. Assoziative Felder werden in verschiedenen Programmiersprachen mit verschiedenen Namen versehen. So spricht man in Java und C++ von einer Map, in Perl und Ruby von einem Hash und wie in Python bezeichnen auch Smalltalk, Objective-C und C# assoziative Arrays als Dictionaries.

Ein Dictionary besteht aus Schlüssel-Objekt-Paaren. Zu einem bestimmten Schlüssel gehört immer ein Objekt. Man kann also die Schlüssel auf die Objekte "abbilden", daher der Kategorienname Mapping.

Dictionaries gehören zu den wichtigsten Datentypen von Python. Kaum ein Programm oder Skript kommt ohne Dictionaries aus. Wie Listen können Dictionaries leicht verändert werden, außerdem können sie beliebig wachsen und schrumpfen während der Laufzeit.

In diesem Kapitel geht es aber nicht nur um Dictionaries sondern am Ende beschäftigen wir uns auch noch mit dem Zusammenhang zwischen Listen und Dictionaries, d.h. wir zeigen wie man aus Dictionaries Listen erzeugt und umgekehrt, wie man aus bestimmten Listen Dictionaries erzeugen kann.

Beispiele für Dictionaries

Unser erstes Beispiel ist ein Dictionary mit deutschen Städten und Einwohnerzahlen, die im Jahre 2019 mehr als eine halbe Million Einwohner hatten. Wir sehen, dass Dictionaries mit geschweiften Klammern geschrieben werden. Sie enthalten Schlüssel-Werte-Paare, die mit Kommata getrennt sind. Ein Schlüssel (englisch key) und sein zugehöriger Wert werden jeweils durch einen Doppelpunkt getrennt.

städte_einwohner = {"Berlin": 3_669_491,
                    "Hamburg": 1_847_253,
                    "München": 1_484_226,
                    "Köln": 1_087_863,
                    "Frankfurt am Main": 763_380,
                    "Stuttgart": 635_911,
                    "Düsseldorf": 621_877,
                    "Leipzig": 593_145,
                    "Dortmund": 588_250,
                    "Essen": 582_760,
                    "Bremen": 567_559,
                    "Dresden": 556_780,
                    "Hannover": 536_925,
                    "Nürnberg": 518_370}

Wir können auf den Wert für einen bestimmten Schlüssel zugreifen, indem wir diesen Schlüssel in Klammern nach dem Namen des Wörterbuchs angeben:

städte_einwohner["Stuttgart"]
Ausgabe: :

635911

Was passiert, wenn wir versuchen, auf einen Schlüssel zuzugreifen, d. h. eine Stadt in unserem Beispiel, die nicht im Dictionary enthalten ist? Wir erheben einen KeyError:

städte_einwohner["Konstanz"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-7-9368e7398149> in <module>
----> 1 städte_einwohner["Konstanz"]
KeyError: 'Konstanz'

Eine häufig gestellte Frage ist, ob Dictionary-Objekte geordnet sind. Die Unsicherheit ergibt sich aus der Tatsache, dass Dictionaries in Versionen vor Python 3.7 nicht sortiert waren. In Python 3.7 und alle späteren Versionen werden Dictionaries nach der Reihenfolge der Item-Eintragung sortiert. In unserem Beispiel bedeutet dies, das unser städte_einwohner-Dictionary die bei der Definition benutzte Reihenfolge beibehält. Dies sieht man, wenn man sich das Dictionary ausgeben lässt:

städte_einwohner
Ausgabe: :

{'Berlin': 3669491,
 'Hamburg': 1847253,
 'München': 1484226,
 'Köln': 1087863,
 'Frankfurt am Main': 763380,
 'Stuttgart': 635911,
 'Düsseldorf': 621877,
 'Leipzig': 593145,
 'Dortmund': 588250,
 'Essen': 582760,
 'Bremen': 567559,
 'Dresden': 556780,
 'Hannover': 536925,
 'Nürnberg': 518370}

Die Tatsache, dass sie geordnet sind, bedeutet aber nicht, dass es eine Möglichkeit gibt, das n-te Element eines Wörterbuchs direkt aufzurufen. Versucht man, auf ein Wörterbuch mit einer Zahl zuzugreifen - wie wir dies mit Listen tun - führt dies zu einer Ausnahme:

städte_einwohner[0]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-9-1c0bd97222ea> in <module>
----> 1 städte_einwohner[0]
KeyError: 0

Wir hatten gesehen, dass wir auch eine Ausnahme erheben, wenn wir einen nicht existierenden Städenamen verwenden. Es ist sehr einfach weitere Einträge einem Dictionary einzufügen. Wir zeigen dies am Beispiel der Stadt Konstanz:

städte_einwohner["Konstanz"] = 84911

Wir können sehen, dass Konstanz ans Ende der Liste eingefügt worden ist:

städte_einwohner
Ausgabe: :

{'Berlin': 3669491,
 'Hamburg': 1847253,
 'München': 1484226,
 'Köln': 1087863,
 'Frankfurt am Main': 763380,
 'Stuttgart': 635911,
 'Düsseldorf': 621877,
 'Leipzig': 593145,
 'Dortmund': 588250,
 'Essen': 582760,
 'Bremen': 567559,
 'Dresden': 556780,
 'Hannover': 536925,
 'Nürnberg': 518370,
 'Konstanz': 84911}

Dies ist also eine Möglichkeit ein Dictionary während des Programmlaufes zu erweitern. Denkbar ist dann auch mit einem leeren Dicitonary zu beginnen. Ein leeres Dictionary wird mit einem geschweiften Klammernpaar {} oder dem Aufruf dict() erzeugt:

food = {}
Ausgabe: :

{}

Alternativ kann man food auch so erzeugen:

In [ ]:
food = dict()

Zu Ehren des Schutzheiligen von Python "Monty Python" erweitern wir nun dieses Dictionary um spezielle pythonische kulinarische Besonderheiten. Was wäre Python ohne "ham", "eggs", "cheese" und "spam"? Wir erweitern unser Dictionary nun inkrementell um diese Delikatessen:

food["ham"] = "yes"
food["eggs"] = "yes"
food["cheese"] = "yes"
food
Ausgabe: :

{'ham': 'yes', 'egg': 'yes', 'spam': 'no', 'cheese': 'yes', 'eggs': 'yes'}

Haben wir da nicht noch etwas vergessen? Leider, denn wer mag schon Spam. Bisher haben wir als Wert "yes" verwendet. Bei "spam" werden wir nun "no" verwenden. Bitte beachte auch, dass die Werte in einem Dictionary nicht eindeutig sein müssen, weshalb wir mehrmals "yes" verwenden konnten. Die Schlüssel sind jedoch eindeutig.

food["spam"] = "no"
food
Ausgabe: :

{'ham': 'yes', 'egg': 'yes', 'spam': 'no', 'cheese': 'yes', 'eggs': 'yes'}

Im nächsten Beispiel kreieren wir ein einfaches Deutsch-Englisches Wörterbuch als Dictionary:

en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
print(en_de)
{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}

Wie wäre es mit einem weiteren Dictionary, eines was von Deutsch nach Französisch übersetzt? Damit sind wir dann aber auch in der Lage von Englisch nach Französisch zu übersetzen, indem wir die beiden Dictionaries hintereinander schalten, wie wir im folgenden Beispiel demonstrieren. Mit Hilfe dieses Dictionarys können wir nun von Englisch nach Französisch übersetzen, obwohl wir gar kein Englisch-Französisch-Wörterbuch haben. So gibt uns de_fr[en_de["red"]] das französische Wort für "red", also "rouge". Dies geschieht in zwei Schritten: Erst wird von Python der Ausdruck en_de["red"] ausgewertet. Das Ergebnis der Auswertung "rot" wird dann im de_fr-Dictionary "nachgeschaut", d.h. de_fr["rot"] liefert nun "rouge" zurück:

en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
print(en_de)
{'red': 'rot', 'green': 'grün', 'blue': 'blau', 'yellow': 'gelb'}
print(en_de["red"])
rot
de_fr = {"rot": "rouge", "grün": "vert", "blau": "bleu", "gelb":"jaune"}
print("The French word for red is: " + de_fr[en_de["red"]])
The French word for red is: rouge

In einem Dictionary können beliebige Typen als Werte verwendet werden. Bei den Schlüsseln gilt jedoch die Einschränkung, dass nur Instanzen unveränderlicher (immutable) Datentypen verwendet werden können, also z.B. keine Listen und keine Dictionaries. Ansonsten erhält man eine Fehlermeldung:

dic = { [1,2,3]:"abc"}
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-af2a40fe8efa> in <module>
----> 1 dic = { [1,2,3]:"abc"}
TypeError: unhashable type: 'list'

Tupel als Schlüssel sind in Ordnung, wie wir im folgenden Beispiel sehen:

dic = { (1,2,3): "abc", 3.1415: "abc"}
dic
Ausgabe: :

{(1, 2, 3): 'abc', 3.1415: 'abc'}

Nun wollen wir unsere Beispiele mit Wörterbüchern für natürliche Sprachen noch ein wenig aufpeppen. Dazu definieren wir eine Dictionary von Dictionaries:

en_de = {"red" : "rot", "green" : "grün", "blue" : "blau", "yellow":"gelb"}
de_fr = {"rot" : "rouge", "grün" : "vert", "blau" : "bleu", "gelb":"jaune"}
 
dictionaries = {"en_de" : en_de, "de_fr" : de_fr }
 
print(dictionaries["de_fr"]["blau"])
bleu

Operatoren auf Dictionaries

Operator
Erklärung
len(d)
liefert die Anzahl aller im Dictionary enthaltenen Elemente, d.h. Schlüssel-Werte-Paare
del d[k]
Löschen des Schlüssels k zusammen mit seinem Wert
k in d
True, wenn es im Dictionary  d einen Schlüssel k gibt
k not in d
True, wenn es im Dictionary d keinen Schlüssel k gibt

Beispiele:

Das folgende Dictionary enthält den Morsecode.

morse = {
"A" : ".-", 
"B" : "-...", 
"C" : "-.-.", 
"D" : "-..", 
"E" : ".", 
"F" : "..-.", 
"G" : "--.", 
"H" : "....", 
"I" : "..", 
"J" : ".---", 
"K" : "-.-", 
"L" : ".-..", 
"M" : "--", 
"N" : "-.", 
"O" : "---", 
"P" : ".--.", 
"Q" : "--.-", 
"R" : ".-.", 
"S" : "...", 
"T" : "-", 
"U" : "..-", 
"V" : "...-", 
"W" : ".--", 
"X" : "-..-", 
"Y" : "-.--", 
"Z" : "--..", 
"0" : "-----", 
"1" : ".----", 
"2" : "..---", 
"3" : "...--", 
"4" : "....-", 
"5" : ".....", 
"6" : "-....", 
"7" : "--...", 
"8" : "---..", 
"9" : "----.", 
"." : ".-.-.-", 
"," : "--..--"
}

Sie können obiges Dictionary als morsecode.py abspeichern, um die folgenden Beispiele leichter nachvollziehen zu können. Zuerst müssen wir das Dictionary wie folgt importieren:

from morsecode import morse

Die Anzahl der verschiedenen Zeichen für die unser Dictionary eine Abbildung in ein Morsezeichen besitzt, können wir mittels len() bestimmen:

len(morse)
Ausgabe: :

38

Unser Dictionary enthält nur Großbuchstaben. "a" in morse liefert deshalb zum Beispiel False zurück:

"a" in morse
Ausgabe: :

False
"A" in morse
Ausgabe: :

True
"a" not in morse
Ausgabe: :

True

pop() und popitem()

pop

Listen können als Stack gesehen und benutzt werden. Dabei wird die pop()-Methode benutzt um ein Element vom Stack zu nehmen. So weit so gut, wenn es um Listen geht, aber macht die pop()-Methode Sinn für Dictionaries. Schließlich ist der dict-Typ kein sequentieller Datentyp, hat also keine Anordnung und keine Indizierung. Aber es gibt dennoch eine pop-Methode für Instanzen des Typs dict. Allerdings ist pop() anders definiert. Falls D ein Dictionary bezeichnet, dann entfernt D.pop(k) den Index k zusammen mit seinem Wert aus dem Dictionary, außerdem liefert D.pop(k) den Wert von D[k] als Rückgabewert zurück. Falls der Schlüssel nicht in D gefunden wird, wird der KeyError generiert:

capitals = {"Österreich":"Wien", "Deutschland":"Berlin", "Niederlande":"Amsterdam"}
capital = capitals.pop("Deutschland")
print(capital)
Berlin
print(capitals)
{'Österreich': 'Wien', 'Niederlande': 'Amsterdam'}
capital = capitals.pop("Schweiz")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-26-1cd938366ca0> in <module>
----> 1 capital = capitals.pop("Schweiz")
KeyError: 'Schweiz'

Der Versuch die Hauptstadt der Schweiz zu ermitteln führte im vorigen Beispiel zum Ausnahmefehler KeyError, weil es diesen Eintrag im Dictionary nicht gibt. Um solche Fehler zu vermeiden, gibt es einen eleganten Weg in Python. Die Methode pop() hat dazu einen zweiten optionalen Parameter, mit dem man einen Default-Wert für diesen Fall mitgeben kann:

capital = capitals.pop("Schweiz","unbekannt")
print(capital) 
unbekannt
capitals.pop("Niederlande","unbekannt")
Ausgabe: :

'Amsterdam'
capitals.pop("Niederlande","unbekannt")
Ausgabe: :

'unbekannt'

popitem

popitem() ist eine Methode von dict, die keine Parameter benötigt und die beliebiges (Schlüssel,Wert)-Paar als Zweiertupel zurückliefert. Wendet man popitem() auf ein leeres Dictionary an, wird der Ausnahmefehler KeyError generiert:

capitals = {"Hessen":"Wiesbaden", "Saarland":"Saarbrücken", "Baden-Württemberg":"Stuttgart", "Rheinland-Pfalz":"Mainz", "Nordrhein-Westfalen":"Düsseldorf"}
(land, capital) = capitals.popitem()
print(land, capital)
Nordrhein-Westfalen Düsseldorf
pair = capitals.popitem()
print(pair)
('Rheinland-Pfalz', 'Mainz')
print(capitals)
{'Hessen': 'Wiesbaden', 'Saarland': 'Saarbrücken', 'Baden-Württemberg': 'Stuttgart'}

Zugriff auf nicht existierende Schlüssel

Versucht man auf nicht existierende Schlüssel zuzugreifen, erhält man eine Fehlermeldung:

woerter = {"house" : "Haus", "cat":"Katze"}
woerter["car"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-33-98bca352453b> in <module>
      1 woerter = {"house" : "Haus", "cat":"Katze"}
----> 2 woerter["car"]
KeyError: 'car'

Man kann dies wie folgt absichern:

if "car" in woerter: print(woerter["car"])
if "cat" in woerter: print(woerter["cat"])
Katze

Eine weitere Methode auf die Werte über die Schlüssel zuzugreifen ist mit der Methode get() gegeben. Auch ihr kann als zweiter Parameter ein Default-Wert mitgegeben werden:

capitals = {"Sachsen":"Dresden", "Niedersachsen":"Hannover", "Brandenburg":"Potsdam"}
capital = capitals["Sachsen"]
print(capital)
Dresden
capital = capitals["Thüringen"]
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-37-aeb15a8a3909> in <module>
----> 1 capital = capitals["Thüringen"]
KeyError: 'Thüringen'
capital = capitals.get("Sachsen")
print(capital)
Dresden
capital = capitals.get("Thüringen")
print(capital) 
None
capital = capitals.get("Thüringen","Erfurt")
print(capital)
Erfurt

Wichtige Methoden

Mit der Methode copy() kann man ein Dictionary kopieren:

w = woerter.copy()
woerter["cat"]="chat"
print(w)
{'house': 'Haus', 'cat': 'Katze'}
print(woerter)
{'house': 'Haus', 'cat': 'chat'}

Bei diesem Kopieren handelt es sich um eine flache (shallow) Kopie. Wenn es sich bei einem Wert um einen komplexen Datentyp, wie z.B. eine Liste oder ein anderes Dictionary handelt, wirken sich Änderungen innerhalb eines solchen Wertes sowohl auf die Kopie als auch auf das Original aus.

# -- coding: utf-8 --

trainings = { "course1":{"title":"Python Training Course for Beginners", 
                         "location":"Frankfurt", 
                         "trainer":"Steve G. Snake"},
              "course2":{"title":"Intermediate Python Training",
                         "location":"Berlin",
                         "trainer":"Ella M. Charming"},
              "course3":{"title":"Python Text Processing Course",
                         "location":"München",
                         "trainer":"Monica A. Snowdon"}
              }
trainings2 = trainings.copy()
trainings["course2"]["title"] = "Perl Training Course for Beginners"
print(trainings2)
{'course1': {'title': 'Python Training Course for Beginners', 'location': 'Frankfurt', 'trainer': 'Steve G. Snake'}, 'course2': {'title': 'Perl Training Course for Beginners', 'location': 'Berlin', 'trainer': 'Ella M. Charming'}, 'course3': {'title': 'Python Text Processing Course', 'location': 'München', 'trainer': 'Monica A. Snowdon'}}

Wenn wir uns die Ausgaben anschauen, sehen wir, dass der Wert von "title" von "course2" sich sowohl in trainings als auch in trainings2 geändert hat.

Alles funktioniert so, wie man sich eine Kopie vorstellt, wenn man einem Schlüssel einen komplett neuen Wert, also ein neues Objekt zuordnet:

trainings = { "course1":{"title":"Python Training Course for Beginners", 
                         "location":"Frankfurt", 
                         "trainer":"Steve G. Snake"},
              "course2":{"title":"Intermediate Python Training",
                         "location":"Berlin",
                         "trainer":"Ella M. Charming"},
              "course3":{"title":"Python Text Processing Course",
                         "location":"München",
                         "trainer":"Monica A. Snowdon"}
              }
trainings2 = trainings.copy()
trainings["course2"] = {"title":"Perl Seminar for Beginners",
                         "location":"Ulm",
                         "trainer":"James D. Morgan"}
print(trainings2["course2"])
{'title': 'Intermediate Python Training', 'location': 'Berlin', 'trainer': 'Ella M. Charming'}

Für diejenigen, die mehr über die tieferen Gründe für dieses Verhalten erfahren wollen, empfehlen wir unser Kapitel über "Flaches und tiefes Kopieren".

Der Inhalt eines Dictionary kann mittels der Methode clear() geleert werden. Das Dictionary wird dabei nicht gelöscht sondern wirklich nur geleert:

trainings.clear()
print(trainings)
{}

Update: Einhängen eines weiteren Dictionary

Mit der Methode update() kann man ein zweites Dictionary in ein Dictionary einhängen. Enthält das zweite Dictionary Schlüssel, die auch im ersten vorkommen, so werden diese mit den Werten des zweiten überschrieben.

w={"house":"Haus","cat":"Katze","red":"rot"}
w1 = {"red":"rouge","blau":"bleu"}
w.update(w1)
print(w)
{'house': 'Haus', 'cat': 'Katze', 'red': 'rouge', 'blau': 'bleu'}

Iteration über ein Dictionary

Es bedarf keiner Methode, um über die Schlüssel eines Dictionarys zu iterieren:

d = {"a":123, "b":34, "c":304, "d":99}
for key in d:
     print(key)
a
b
c
d

Man kann aber auch die Methode keys() benutzen, die einem speziell die Schlüssel liefert:

for key in d.keys():
     print(key)
a
b
c
d

Mit der Methode values() iteriert man direkt über die Werte:

for value in d.values():
     print(value)
123
34
304
99

Wenn man nur auf die Ergebnisse schaut, ist das äquivalent zu der folgenden Schleife:

for key in d:
    print(d[key])
123
34
304
99

Was die Implementierung und die Effizienz betrifft, ist die letzte Methode jedoch weniger effizient. Im Folgenden zeigen wir, dass der erste Weg deutlich schneller ist. Um das Folgende zu verstehen, sollte man sich mit %%timeit von ipython auskennen:

%%timeit  d = {"a":123, "b":34, "c":304, "d":99}
for key in d.keys():
    x=d[key]
    x
564 ns ± 54.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%%timeit  d = {"a":123, "b":34, "c":304, "d":99}
for value in d.values():
    x=value
    x
371 ns ± 36.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

Zusammenhang zwischen Listen und Dictionaries

Reißverschluss auf Kugel in Anspielung auf die Python-Funktion zip.

Wenn man eine Weile mit Listen und Dictionaries unter Python gearbeitet hat, kommt nahezu zwangsläufig der Punkt, dass man Listen in Dictionaries oder umgekehrt Dictionaries in Listen wandeln will bzw. muss. Es wäre nicht allzu schwer, direkt mit den bisher bekannten Funktionen sich neue Funktionen zu schreiben, die genau dies bewerkstelligen. Aber Python wäre nicht Python, wenn es hierfür nicht auch Hilfen zur Verfügung stellen würde.

Es bietet sich an, ein Dictionary der Art

{"list":"Liste", "dictionary":"Wörterbuch", "function":"Funktion"}
als Liste von 2-Tupel darzustellen.
[("list","Liste"), ("dictionary","Wörterbuch"), ("function","Funktion")]

Listen oder Mengen aus Dictionaries generieren

Immer wieder kommt es vor, dass man ein Dictionary hat und man aus diesem Dictionary Mengen oder Listen erzeugen will. Betrachten wir dazu das folgende Dictionary mit englisch-deutschen Farben:

colours = {"red":"rot", "green":"grün", "blue":"blau", "yellow":"gelb"}

Nehmen wir nun an, dass wir daraus gerne die Menge aller Schlüssel, also alle englischen Farbadjektive extrahieren wollen. Dazu bietet die Klasse dict die Methode "keys" an. Zu dieser Methode sagt uns die help-Funktion help(dict.keys):

help(dict.keys)
Help on method_descriptor:
keys(...)
    D.keys() -> a set-like object providing a view on D's keys

Zu Deutsch bedeutet dies, dass D.Keys für ein Dictionary D ein Mengen-ähnliches Objekt zurückliefert, was uns eine "view" (Sicht) auf die Schlüssel (keys) von D liefert. Eine "view" ist ein spezielles Python-Objekt, was in diesem Fall, wie die Hilfe sagt, ein einer Menge ähnelndes Objekt erzeugt. Dieses Objekt ist aber ein Iterator, der seine Elemente aus dem Dictionary generiert, ohne dass eine Menge generiert wird. Es erfolgt also keine Kopie.

english_colours = colours.keys()
english_colours
Ausgabe: :

dict_keys(['red', 'green', 'blue', 'yellow'])

Im folgenden Beispiel können wir sehen, dass sich diese View auch "ändert", wenn das Dictionary geändert wurde:

colours["black"] = "schwarz"
english_colours
Ausgabe: :

dict_keys(['red', 'green', 'blue', 'yellow', 'black'])

Möchte man aus dem dict_keys-Objekt eine "echte" Menge oder eine Liste erzeugen, so kann man dies mit den globalen Funktionen set und list bewerkstelligen:

english_colours_set = set(english_colours)
colours["white"] = "weiß"
english_colours
Ausgabe: :

dict_keys(['red', 'green', 'blue', 'yellow', 'black', 'white'])
english_colours_set
Ausgabe: :

{'black', 'blue', 'green', 'red', 'yellow'}
english_colours_list = list(english_colours)
english_colours_list
Ausgabe: :

['red', 'green', 'blue', 'yellow', 'black', 'white']

Analog dazu kann man sich auch eine view für die Werte eines Dictionaries (dict_values) mittels der Methode values erzeugen:

german_colours = colours.values()
german_colours
Ausgabe: :

dict_values(['rot', 'grün', 'blau', 'gelb', 'schwarz', 'weiß'])

Außerdem gibt es noch die methode items(), mit der man sich eine View der Schlüssel-Werte-Paare erzeugen kann:

pairs = colours.items()
pairs
Ausgabe: :

dict_items([('red', 'rot'), ('green', 'grün'), ('blue', 'blau'), ('yellow', 'gelb'), ('black', 'schwarz'), ('white', 'weiß')])
set(pairs)
Ausgabe: :

{('black', 'schwarz'),
 ('blue', 'blau'),
 ('green', 'grün'),
 ('red', 'rot'),
 ('white', 'weiß'),
 ('yellow', 'gelb')}
list(pairs)
Ausgabe: :

[('red', 'rot'),
 ('green', 'grün'),
 ('blue', 'blau'),
 ('yellow', 'gelb'),
 ('black', 'schwarz'),
 ('white', 'weiß')]

Semantisch gesehen beinhaltet eine Liste/View, die mittels der Methode items() aus einem Dictionary erzeugt wurde, den kompletten Informationsgehalt des ursprünglichen Dictionary. Allerdings gibt es keine Zugriffsfunktionen über die Schlüssel (Keys), d.h. die erste Komponente der 2-Tupel und der Werte (Values), also der zweiten Komponente der 2-Tupel.

Listen in Dictionaries wandeln

Auch wenn wir uns im folgenden ein wenig mit Essen beschäftigen, bleibt dies ein Python-Kurs und wird kein Kochkurs. Nehmen wir an, dass wir zwei Listen haben, von denen eine die Keys und die andere die Werte enthält:

gerichte = ["Pizza", "Sauerkraut", "Paella", "Hamburger"]
laender = ["Italien", "Deutschland", "Spanien", "USA"]

Nun wollen wir ein Dictionary erzeugen, dass einem Gericht ein Land zuordnet. (Sorry, dass wir hier die Vorurteile bedienen.) Dazu benötigen wir die Funktion zip(). Der Name zip wurde gewählt, weil zip "Reißverschluss" im Englischen bedeutet. Wie ein Reißverschluss nimmt zip zwei oder mehr Listen - genauer gesagt iterierbare Objekte - als Argumente und liefert einen Iterator zurück, der die i-ten Komponenten der Argumente zu Tupeln zusammenfasst.

In unserem kulinarischen Beispiel sieht das wie folgt aus:

gericht_land_iterator = zip(laender, gerichte)
gericht_land_iterator
Ausgabe: :

<zip at 0x2e61378b108>
land_gericht = list(gericht_land_iterator)
land_gericht
Ausgabe: :

[('Italien', 'Pizza'),
 ('Deutschland', 'Sauerkraut'),
 ('Spanien', 'Paella'),
 ('USA', 'Hamburger')]

In der Variablen land_gericht haben wir nun eine Liste, in der die Elemente 2-Tupel sind mit den Schlüsseln und den Werten. Logisch gesehen sind wir also einem Dictionary schon sehr nahe. Was wir aber eigentlich wollen, ist ein richtiges Python-Dictionary. Glücklicherweise gibt es auch hierfür eine Funktion. dict() nimmt als Argument eine Liste der obigen Form und wandelt sie in ein Dictionary um:

land_gericht_dict = dict(land_gericht)
print(land_gericht_dict)
{'Italien': 'Pizza', 'Deutschland': 'Sauerkraut', 'Spanien': 'Paella', 'USA': 'Hamburger'}

Allerdings ist das obige Vorgehen äußerst ineffizient, da wir zuerst eine Liste mit Zweiertupel erzeugen und diese dann in ein Dictionary wandeln. Man kann "dict" auch direkt auf das Ergebnis von zip aufrufen:

gerichte = ["Pizza", "Sauerkraut", "Paella", "Hamburger"]
laender = ["Italien", "Deutschland", "Spanien", "USA"]
dict(zip(laender, gerichte))
Ausgabe: :

{'Italien': 'Pizza',
 'Deutschland': 'Sauerkraut',
 'Spanien': 'Paella',
 'USA': 'Hamburger'}

Eine Frage stellt sich noch bezüglich der Funktion zip(). Was passiert, wenn eine der beiden Listen mehr Elemente als die andere hat? Ganz einfach, die überzähligen Elemente werden nicht verarbeitet, d.h. sie kommen nicht in die Ergebnisliste.

laender = ["Italien","Deutschland","Spanien","USA","Schweiz"]
gerichte = ["Pizza", "Sauerkraut", "Paella", "Hamburger"]
land_gericht = zip(laender,gerichte)
print(land_gericht)
<zip object at 0x000002E611828E48>

Die brennende Frage, was wohl das Nationalgericht der Schweiz ist, bleibt, zumindest was diesen Kurs betrifft unbeantwortet!

Alles in einem Schritt

Normalerweise empfehlen wir nicht zu viele Schritte in eine Anweisung oder einen Ausdruck zu stecken, auch wenn dies eindrucksvoll ausschauen mag und der Code dadurch sehr klein und kompakt wird. Es ist häufig besser Zwischenschritte mit sprechenden Variablen einzubauen. Dadurch wird die Lesbarkeit und Verständlichkeit eines Programmes erhöht.

Unser vorheriges kulinarisches Dictionary könnten wir in einem Schritt schreiben:

country_specialities_dict = dict(zip(["pizza", "sauerkraut", "paella", "hamburger"], ["Italy", "Germany", "Spain", "USA"," Switzerland"]))
print(country_specialities_dict)
{'pizza': 'Italy', 'sauerkraut': 'Germany', 'paella': 'Spain', 'hamburger': 'USA'}

Andererseits sind die Zwischenschritte im folgenden Code möglicherweise des Guten zu viel:

dishes = ["pizza", "sauerkraut", "paella", "hamburger"]
countries = ["Italy", "Germany", "Spain", "USA"]
country_specialities_zip = zip(dishes,countries)
country_specialities_list = list(country_specialities_zip)
country_specialities_dict = dict(country_specialities_list)
print(country_specialities_dict)
{'pizza': 'Italy', 'sauerkraut': 'Germany', 'paella': 'Spain', 'hamburger': 'USA'}

Wir erhalten das Gleiche Ergebnis, wie im Einzeiler.

Lauernde Gefahr

Insbesondere für diejenigen, die von Python 2.x auf Python 3.x wechseln lauert im obigen Vorgehen eine große Gefahr: zip() lieferte in Python2 eine Liste zurück. In Python 3.x hingegen liefert zip nun einen Iterator zurück. Man sollte sich immer vergegenwärtigen, dass sich viele Iteratoren bei der Benutzung "verbrauchen", das bedeutet, dass man sie nur einmal durchlaufen kann.

Wir demonstrieren dies im folgenden kleinen Beispiel:

l1 = ["a","b","c"]
l2 = [1,2,3]
c = zip(l1, l2)
for i in c:
    print(i)
('a', 1)
('b', 2)
('c', 3)
for i in c:
    print(i)

Diesen Effekt kann man auch beobachten, wenn man list() benutzt:

l1 = ["a","b","c"]
l2 = [1,2,3]
c = zip(l1,l2)
z1 = list(c)
z2 = list(c)
print(z1)
[('a', 1), ('b', 2), ('c', 3)]
print(z2)
[]

Als Übung können Sie überlegen, was im folgenden Skript faul ist:

dishes = ["pizza", "sauerkraut", "paella", "hamburger"]
countries = ["Italy", "Germany", "Spain", "USA"]
country_specialities_zip = zip(dishes,countries)
print(list(country_specialities_zip))
country_specialities_list = list(country_specialities_zip)
country_specialities_dict = dict(country_specialities_list)
print(country_specialities_dict)
[('pizza', 'Italy'), ('sauerkraut', 'Germany'), ('paella', 'Spain'), ('hamburger', 'USA')]
{}

Wenn man das Skript startet, sieht man, dass das Dictionary, was wir erzeugen wollen, leer ist.