Python Dictionaries (dict)                  (C) 2020-2025 T.Birnthaler OSTC GmbH
==========================

Doku --> docs.python.org/3/library/stdtypes.html#dict

Dictionaries (dict) (Map, Hash, Assoziatives Array, Zuordnung, Verzeichnis)
speichern PAARE/ITEMS aus KEY + VALUE und damit eine "Beziehung" zwischen
zwei Datenwerten (nur die Beziehung in der Richtung KEY --> VALUE).

Ein Dictionary entspricht einer 2-spaltigen Datenbank-Tabelle mit festen
Spaltennamen KEY + VALUE und enthält als Datensätze KEY-VALUE-PAARE. Die Spalte
KEY ist UNIQUE und INDIZIERT, d.h. der Zugriff über diese Spalte ist unabhängig
von der Anzahl der Datensätze und sehr schnell. Die Spalte VALUE ist NON-UNIQUE
und nicht INDIZIERT, d.h. der Zugriff über diese Spalte ist abhängig von der
Anzahl der Datensätze und relativ langsam.

Das Dictionary liegt im Speicher und stirbt am Ende des Programmes, daher es
ist NICHT PERSISTENT, es gibt nur einen Zugriffspfad (keine MULTI-USER
Fähigkeit) und Zugriffe darauf sind WESENTLICH SCHNELLER als auf echte
Datenbanken (keine SQL-Statements erzeugen, versenden, parsen und beantworten,
keine NW-Verbindung, kein Protokoll-Overhead, keine Plattenzugriffe, ...).

Die Datensätze sind UN-GEORDNET (seit Python 3.7 bleibt die Einfüge-Reihenfolge
erhalten) und UN-NUMERIERT. Die DATENTYPEN der beiden Spalten sind nicht
festgelegt und können daher beliebig gemischt sein. Im KEY können aber nur
IM-MUTABLE Datentypen (besser HASHABLE) Datentypen gespeichert werden, im VALUE
sind alle Python-Datentypen erlaubt. Die Spalte VALUE kann per
CONTAINER-Datentyp tuple, list, set, dict, ... auch MEHRSPALTIG und sogar
HIERARCHISCH gefüllt sein.

  d1 = {}    oder   d1 = dict()                     # Leeres Dictionary
  d2 = {"abc": 123, True: 4.0, (1,2,3): None}       # 3 Paare/Items
  d3 = {"a": 1, "b": 2, "c": 1, "d": 3}             # 4 Paare/Items
  d4 = dict(a=1, b=2, c=1, d=3)                     # Alternative Schreibweise
  d5 = dict([("a",1), ("b",2), ("c",1), ("d",3)])   # Alternative Schreibweise

* KEYS müssen IM-MUTABLE Objekte bzw. eigentlich HASHABLE Objekte sein
  (NoneType, bool, int, float, complex, str, tuple, frozenset, bytes).

* KEYS sind UNIQUE (d.h. der gleiche Key kann nur 1x vorkommen).

* Als VALUE sind beliebige PYTHON-OBJEKTE erlaubt.

* VALUES dürfen NON-UNIQUE sein (d.h. der gleiche Value darf mehrfach vorkommen).

* Dictionaries sind bezüglich KEY/VALUE UN-GEORDNET (seit Python 3.7 bleibt die
  Einfüge-Reihenfolge erhalten). Will man einen Zugriff in spezieller Reihenfolge,
  muss man beim Zugriff MANUELL sortieren.

* Die ZUGRIFFSGESCHWINDIGKEIT auf einen bestimmten KEY (und damit seinen VALUE)
  ist UN-ABHÄNGIG von der Dictionary-Größe immer gleich SCHNELL (RANDOM ACCESS).

* Die ZUGRIFFSGESCHWINDIGKEIT auf einen bestimmten VALUE ist ABHÄNGIG von der
  Dictionary-Größe und immer LANGSAM (da sie das Durchsuchen des gesamten
  Dictionarys erfordert).

Folgende Operation werden von Dictionaries unterstützt (K ist ein Key =
im-mutable Objekt, V und DFLT sind Values = beliebige Objekte, D, D2 und E sind
Dictionaries, ITBL ist ein Iterable, L ist eine Liste):

ACHTUNG: Nur LESENDE Operationen, das Dictionary D bleibt UNVERÄNDERT!

+------------------------+-----------------------------------------------------+
| for K in D: ...        | Alle Keys K von D durchlaufen (ungeordnet)          |
| for K in D.keys(): ... | Alle Keys K von D durchlaufen (ungeordnet)          |
| for V in D.values():   | Alle Values V von D durchlaufen (ungeordnet)        |
| for (K,V) in D.items() | Alle Keys+Values K+V von D durchlaufen (ungeordnet) |
+------------------------+-----------------------------------------------------+
| D == D2                | Dictionaries gleich (alle Key-Value Paare)?         |
| D != D2                | Dictionaries un-gleich (ein Key oder Value)?        |
+------------------------+-----------------------------------------------------+
| K in D                 | Key K in D enthalten?                               |
| K not in D             | Key K in D nicht enthalten?                         |
+------------------------+-----------------------------------------------------+
| len(D)                 | Anzahl Keys/Values/Paare/Items                      |
+------------------------+-----------------------------------------------------+
| V = D[K]               | Value von Key K holen (KeyError falls Key fehlt)    |
| V = D.get(K, DFLT)     | Value von Key K holen (oder DFLT, kein KeyError)    |
+------------------------+-----------------------------------------------------+
| L = D.keys()           | Liste aller Keys (ungeordnet)                       |
| L = D.values()         | Liste aller Values (ungeordnet)                     |
| L = D.items()          | Liste aller (Key,Value)-Paare/Items (ungeordnet)    |
+------------------------+-----------------------------------------------------+
| L = list(D)            | Abkürzung für D.keys()                              |
| L = iter(D)            | Abkürzung für iter(D.keys())                        |
| L = reversed(D)        | Abkürzung für reversed(D.keys())                    | PY3.8
+------------------------+-----------------------------------------------------+
| D2 = D.copy()          | Shallow/Flache Kopie                                |
+------------------------+-----------------------------------------------------+
| E = D | D2             | D2 zu D hinzufüg, D2-Val überschreibt D bei gl. Key | PY3.9
+------------------------+-----------------------------------------------------+

ACHTUNG: SCHREIBENDE Operationen,
         das Dictionary D wird VERÄNDERT (bleibt aber das gleiche Objekt)!

+-----------------------+------------------------------------------------------+
| D[K] = V              | Value V für Key K setzen (oder ersetzen)             |
| D.setdefault(K, DFLT) | Value zu Key K auf DFLT setzen (falls NICHT vorhand.)|
|                       | + Value von Key zurückgeben                          |
+-----------------------+------------------------------------------------------+
| del D[K]              | Key K + Value löschen (oder KeyError falls Key fehlt)|
| V = D.pop(K, DFLT)    | Value V von Key K holen (KeyError/DFLT) + Key löschen|
| (K,V) = D.popitem()   | Paar/Item (K,V) holen + entf. (KeyError falls D leer)|
+-----------------------+------------------------------------------------------+
| D = dict.fromkeys(I)  | Dict D mit Keys aus Iterable I + Wert None erzeugen  |
| D = dict.fromkeys(I,V)| Dict D mit Keys aus Iterable I + Wert V erzeugen     |
| D.update(D2|ITER)     | D2/ITER zu D hinzufügen (überschreibt D bei gl. Key) |
| D.clear()             | Dict D leeren (alle Keys + Values entfernen)         |
+-----------------------+------------------------------------------------------+
| D |= D2               | D2 zu D hinzufügen, D2-Val überschreibt D bei gl. Key| PY3.9
+-----------------------+------------------------------------------------------+
ITER = Iterable mit PAAREN als Elemente (z.B. Liste von 2-er Tupeln)

HINWEIS: Zugriff auf ein Dictionary mit einem nicht vorhandenen Key löst die
         Exception "KeyError" aus.

HINWEIS: Bei Funktion "fromkeys" muss "dict." als Präfix verwendet werden, da
noch kein Dictionary-Objekt vorhanden ist, auf das "fromkeys" angewendet werden
kann (sondern durch "fromkeys" erst erzeugt wird).

HINWEIS: Seit Python 3.7 entspricht die Key-Reihenfolge im Dictionary der
EINFÜGE-REIHENFOLGE und bleibt bei Operationen auf dem Dictionary erhalten.
Vorher war die Reihenfolge der Keys ZUFÄLLIG und konnte sich jederzeit ändern.

HINWEIS: Die Keys "True/False" in Dictionaries werden mit den Keys 1/0 (int),
1.0/0.0 (float) und 1+0j/0+0j (complex) IDENTIFIZIERT (d.h. jeder der folgenden
Keys kann nur 1x in einem Dictionary vorkommen).

  True    1   1.0   1+0j   1+0.0j   1.0+0.0j
  False   0   0.0   0+0j   0+0.0j   0.0+0.0j

Vor PY3.7 war das ein ZUFÄLLIGER Key + Wert, seit PY3.7 ist es der ERSTE im
Dictionary vorkommende Key mit dem LETZTEN diesem Key zugeordneten Wert:

  D = {True: "bool", 1: "int", 1.0: "float", 1+0j: "complex"}
  print(D)              # --> {True: 'complex'}
  D = {1: "int", True: "bool", 1.0: "float", 1+0j: "complex"}
  print(D)              # --> {1: 'complex'}
  D = {0+0j: "complex", 0: "int", False: "bool", 0.0: "float"}
  print(D)              # --> {0+0j: 'float'}
  D = {0.0: "float", 0+0j: "complex", 0: "int", False: "bool"}
  print(D)              # --> {0.0: 'bool'}