Dictionaries in Python

Dictionaries gehören zu den zentralen Datentypen in Python. Wie ein Wörterbuch das Begriffe und deren Bedeutungen verknüpft, enthält das Python-Dictionary Elemente, die mit weiterer Information verknüpft sind. In Python spricht man bei Dictionaries ganz Allgemein von Key-Value oder auch Schlüssel-Wert Paaren. Solche Key-Value-Paare können beispielsweise sein:

  • Codierungen in einem Fragebogen
  • KFZ-Kennzeichen-Codes für Kreise
  • HTTP-Statuscodes
  • Windkraftanalagen und ihre Geopositionen
  • Zeitstempel und erfasste Leistung von Maschinen

In diesem Beitrag wird der Datentyp Dictionary im Detail vorgestellt und zentrale Eigenschaften und Anwendungen beschrieben.

Inhalt

Erstellen von Dictionaries

Um ein Python-Dictionary zu erzeugen, gibt es 2 Möglichkeiten: Die literale Initiierung und die funktionale Initiierung. Den Weg der literalen Initiierung wählt man in der Regel, wenn auf statische Weise (bspw. per Handeingabe in der Console) kleine Objekte mit wenigen Schlüssel-Wert-Paaren erzeugt werden. In Programmen werden Dictionaries eher auf dynamische (generische) Weise erzeugt. Dafür benötigt man den funktionalen Weg.

Die Keys in einem Dictionary müssen die Bedingung eines Immutable Objects erfüllen. D.h. der Key kann z.B. ein String, Int, Tupel oder Float sein. Das Objekt eines Dictionary-Werts kann jeden Typs sein.

1. Literale Initiierung

Die folgende Abbildung zeigt das syntaktische Schema des Dictionary-Literals:

Hier erzeugen wir uns ein Beispieldictionary mit 3 Einträgen zu HTTP-Statuscodes.

myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'}  
>>>{200: 'OK', 404: 'Not Found', 502: 'Bad Gateway'}

2. Funktionale Initiierung

Mit der Funktion dict werden Dictionaries ebenfalls erzeugt. Gegenüber der literalen Initiierung weist die Funktion allerdings einige syntaktische Tücken auf. Werden ihr Key-Value Zuordnungen auf diese Weise übergeben:

dict(A=1,B=2,C=3)
>>>{'A': 1, 'B': 2, 'C': 3}

müssen die Keys den Python Identifier-Regeln entsprechen. D.h. sie müssen mit einem Unterstrich oder einem Buchstaben beginnen. Die Funktion übersetzt die Keys intern schließlich in Strings.

Um über den Funktionsaufruf ein Dictionary zu erstellen, welches einen anderen Typ von Keys enthält, müssen der Funktion Key-Value-Paare als Tupel in Listenform übergeben werden.

dict([(1,'111'), (2,'222'), (3,'333'),])
>>>{1: '111', 2: '222', 3: '333'}

Die dict-Funktion ist insbesondere dann wichtig, wenn aus Listen ein Dictionary erzeugt werden soll.

code = [1,2,3,4,5]
item = ['Stimme voll zu', 'Stimme eher zu', 'Unentschlossen', 'Stimme eher nicht zu', 'Stimme überhaupt nicht zu']
zipped = zip(code, item)

print(dict(zipped))
>>>{1: 'Stimme voll zu', 2: 'Stimme eher zu', 3: 'Unentschlossen', 4: 'Stimme eher nicht zu', 5: 'Stimme überhaupt nicht zu'}

Datenzugriffe

Normalerweise erfolgt der Zugriff auf das Dictionary über die Schlüssel. Innerhalb von eckigen Klammern (über die in Python üblicherweise Positionen indiziert werden) wird dieser an das Dictionary gehangen und dient dann als Filter. Die Rückgabe eines solchen Ausdrucks ist der zum Schlüssel gehörige Wert.
Natürlich gibt es auch die Möglichkeit über einen Wertefilter die zuhörigen Keys aus dem Dictionary zu erfragen. Diese Variante wird im Beispielcode ebenfalls gezeigt:

myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'}  

# --- Zugriff auf Werte über Key
# Einzelnes Item
myDict[200]
>>> OK

# Mehrere Items
items  = [200,502]
values = [myDict[i] for i in items]
print(values)
>>> ['OK', 'Bad Gateway']

# --- Zugriff auf Key über Werte
values = ["Not Found", "OK"]
keys = [k for k, v in myDict.items() if v in values]
print(keys)
>>> [200, 404]

Zuweisungen

Zuweisungen erfolgen üblicherweise über den Zugriff auf ein Key-Element und ändern den entsprechenden Wert. In dem Beispielcode wird der Wert für den Key 200 geändert. Änderungen an den Keys können natürlich ebenfalls vorgenommen werden. Dafür bietet sich die Method pop an (siehe unten: Methoden von Dictionaries).

# --- Wertzuweisung 
myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'}  

myDict[200] =  '__OK__'
print(myDict)
>>> {200: '__OK__', 404: 'Not Found', 502: 'Bad Gateway'}

item = {200:"__NOT OK__"}
myDict.update(item)
print(myDict)
>>> {200: '__NOT OK__', 404: 'Not Found', 502: 'Bad Gateway'}

# --- Keyzuweisung
myDict[2000] = myDict.pop(200)
print(myDict)
>>> {2000: '__NOT OK__', 404: 'Not Found', 502: 'Bad Gateway'}

Iterieren über Dictionaries

Iterationen über Dictionaries erfolgen im einfachsten Fall über die Keys.

myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'} 
for i in myDict:
    print(i)

>>> 200
>>> 404
>>> 502

Eine häufige Anwendung ist es jedoch, innerhalb der Iterationen neben dem Zugriff auf den Schlüssel auch den Wert anzusprechen. Dies gelingt mit der Methode items. Sie erzeugt aus dem Dictionary eine Liste mit Tupeln, über die iteriert wird.

myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'} 

# Zur Veranschaulichung wird hier die Ausgabe von items() angezeigt. Über dieses Objekt wird in der unten 
# aufgeführten Schleife iteriert.
print(myDict.items())
>>> dict_items([(200, 'OK'), (404, 'Not Found'), (502, 'Bad Gateway')])

for key, value in myDict.items():
    print(key, value)

>>> 200 OK 
>>> 404 Not Found
>>> 502 Bad Gateway

Wie für den Datentyp Liste existiert für Dictionaries eine Kurzschreibweise von Schleifen: Die Dict-Comprehensions. Im ersten Codebeispiel werden mit Hilfe der Dict-Comprehension die Keys in einem String modifiziert. Das zweite Beispiel erzeugt aus 2 Listen ein Dicitonary.

myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'} 
{str(k) + '__SUFFIX': v for k, v in myDict.items()}
>>> {'200__SUFFIX': 'OK', '404__SUFFIX': 'Not Found', '502__SUFFIX': 'Bad Gateway'}


keys   = [200, 404, 502]
values = ['OK', 'Not Found', 'Bad Gateway']
{v: values[i] for i, v in enumerate(keys)}
>>> {200: 'OK', 404: 'Not Found', 502: 'Bad Gateway'}

Dictionaries im Speicher

Dictionaries ähneln in ihrer Python-Implementierung dem Typ Liste. Wie auch Listen sind Dictionaries Mutable Objects, d.h. auch sie verändern ihren physikalischen Speicherort nicht, wenn Änderungen an ihnen vorgenommen werden. Und wie Listen auch, enthalten Dictionaries nicht die Objekte selbst, sondern lediglich deren Referenzen. Im Gegensatz zu Listen sind Dictionaries allerdings nicht sortiert. Ihre Werte sind lediglich über die entsprechenden Schlüssel ansprechbar, nicht aber über eine Position.

Aus dem Beispielcode lassen sich die oben genannten Eigenschaften zeigen. Sowohl das leere Dictionary myDict als auch das mit 5 Key-Value-Paaren befüllte Dictionary myDict haben eine Größe von 288 Bytes. Der Speicherort hat sich durch das Befüllen nicht geändert. Außerdem sehen wir, dass die Größe aller Values mit 297 Bytes die Größe der Liste überschreitet und es somit ausgeschlossen ist, dass das Dictionary selbst die Objekte enthält.

import sys

myDict = {} # Erstelle leeres Dictionary 
print(id(myDict))
>>>2239975926472 # Physikalischer Speicherort von myDict

sys.getsizeof(myDict)
>>>288 # Größe von myDict

# Befüllen von myDict
myDict2 = {200:'OK', 404:'Not Found', 502:'Bad Gateway', 403:'Forbidden', 500:'Internal Server Error'} 
myDict.update(myDict2)

print(id(myDict))
>>>2239975926472

print(sys.getsizeof(myDict))
>>>288

# --- Summiere die Größe aller im Dictionary enthaltenen Werte
total_size_values = 0
for k, v in myDict.items():
    total_size_values += sys.getsizeof(v)
print(total_size_values)
>>>297

Unser Dictionary enthält im Moment 5 Key-Value-Paare und hat damit den Speicherpuffer erschöpft. Wir sehen dies, indem wir ein weiteres Key-Value-Paar an das Dictionary anhängen und erneut die Größe des Dictionaries abfragen. Diese hat sich von 280 Bytes auf 480 Bytes erhöht:

new_item = {202: "Accepted"}
myDict.update(new_item)

print(sys.getsizeof(myDict))
>>> 480

Folgendes Schaubild zeigt noch einmal das Speichermanagement von Dictionaries in schematischer Form:

Methoden von Dictionaries

Die wichtigsten Methoden von Dictionaries im Überblick:

Methode

Beispiel

Funktionsweise

clear myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict.clear()

print(myDict)
>>>{}

Entfernt alle Schlüssel-Wert-Paare aus dem Dictionary.
copy myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

y = myDict
print(id(myDict))
>>>2239975864840
print(id(y))
>>>2239975864840

y = myDict.copy()
print(id(myDict))
>>>2239975864840
print(id(y))
>>>2234552213903

Erstellt eine Kopie des Dictionaries im Speicher.
fromkeys myDict = {}
myDict = myDict.fromkeys((‚A‘,’B‘,’C‘), 0)
print(myDict)
>>>{‚A‘: 0, ‚B‘: 0, ‚C‘: 0}

dict.fromkeys([1,2,3])
>>>{1: None, 2: None, 3: None}

Erstellt ein Dictionary aus einem Tupel oder einer Liste von Immutable-Objects. Jedem Schlüssel wird ein Default-Wert zugewiesen. Da es sich um eine Klassenmethode handelt, ist auch die Eingabe dict.fromkeys() möglich.
get myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

print(myDict.get(200))
>>>OK

Holt den Wert über die Eingabe des Schlüssels.
items myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict.items()
>>>dict_items([(200, ‚OK‘), (404, ‚Not Found‘), (502, ‚Bad Gateway‘)])

Wandelt das Dictionary in eine Liste von Tupeln um. Jedes Tupel entspricht einem Schlüssel-Wert-Paar. Die Methode wird zum iterieren über Dictionaries verwendet.
keys myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict.keys()
>>>dict_keys([200, 404, 502])

Gibt die Keys des Dictionaries zurück.
pop myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict.pop(200)
print(myDict)
>>>{404: ‚Not Found‘, 502: ‚Bad Gateway‘}

Entfernt ein Schlüssel-Wert-Paar aus dem Dictionary.
popitem myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict.popitem()
>>>(200, ‚OK‘)

Entfernt ein zufälliges Schlüssel-Wert-Paar aus dem Dictionary.
setdefault myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

print(myDict.setdefault(200))
>>>OK
print(myDict)
>>>{200: ‚OK‘, 404: ‚Not Found‘, 502: ‚Bad Gateway‘}

print(myDict.setdefault(403,’Forbidden‘))
>>>Forbidden
print(myDict)
>>>{200: ‚OK‘, 403: ‚Forbidden‘, 404: ‚Not Found‘, 502: ‚Bad Gateway‘}

print(myDict.setdefault(200,’TEST‘))
>>>OK
print(myDict)
>>>{200: ‚OK‘, 404: ‚Not Found‘, 502: ‚Bad Gateway‘}

Holt den Wert über die Eingabe des Schlüssels (vergleichbar mit der Methode get). Nur wenn der Schlüssel nicht im Dictionary vorhanden ist, wird ein entsprechendes Schlüssel-Wert-Paar im Dictionary erstellt.
update myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict2= {100:’Continue‘, 400:’Bad Request‘}
myDict.update(myDict2)
print(myDict)
>>>{200: ‚OK‘, 400: ‚Bad Request‘, 404: ‚Not Found‘, 502: ‚Bad Gateway‘, 100: ‚Continue‘}

myDict3 = {200:’ok‘}
myDict.update(myDict3)
print(myDict)
>>>{400: ‚Bad Request‘, 404: ‚Not Found‘, 502: ‚Bad Gateway‘, 200: ‚ok‘, 100: ‚Continue‘}

Hängt ein Dictionary an ein anderes Dictionary an. Bei Schlüssel-Duplikaten wird der Wert mit dem angehangenen Dictionary überschrieben.
values myDict = {200:’OK‘, 404:’Not Found‘, 502:’Bad Gateway‘}

myDict.values()
>>>dict_values([‚OK‘, ‚Not Found‘, ‚Bad Gateway‘])

Gibt alle Werte des Dictionaries aus.

Weitere Funktionen auf Dictionaries

myDict = {200:'OK', 404:'Not Found', 502:'Bad Gateway'}

len() – Die Länge des Dictionaries ermitteln

len(myDict)

del – Key-Value-Paare aus dem Dictionary löschen

del myDict[404]
print(myDict)
>>>{200: 'OK', 502: 'Bad Gateway'}

in-Operator – Prüft, ob ein Schlüssel im Dictionary enthalten ist

404 in myDict
>>>True

Arithmetische Operationen auf Dictionaries

myDict = {'A':100, 'B':200, 'C':300} 
max(myDict.values()) # Wird die Methode values nicht angewendet, wird die Funktion auf die Keys angewendet.
min(myDict.values())

# Das Modul statistics verfügt über zahlreiche Funktionen aus der Deskriptiven Statistik 
from statistics import mean, median, stdev, variance
mean(myDict.values())
median(myDict.values())
stdev(myDict.values())
variance(myDict.values())