W3docs

Python collections-Modul

Das Python-collections-Modul erklärt: Counter, defaultdict, namedtuple, deque, OrderedDict und ChainMap mit praktischen Beispielen.

Das eingebaute Python-Modul collections bietet spezialisierte Container-Typen, die die standardmäßigen Typen list, dict und tuple erweitern oder ersetzen. Jeder Typ löst ein bestimmtes Problem, das andernfalls mehrere zusätzliche Zeilen manuellen Verwaltungsaufwands erfordern würde.

Dieses Kapitel behandelt alle sechs häufig verwendeten Typen: Counter, defaultdict, namedtuple, deque, OrderedDict und ChainMap. Für jeden Typ wird gezeigt, welches Problem er löst, wie man ihn erstellt und verwendet und worauf man achten sollte.

Es ist keine Installation erforderlich — collections ist in jeder Python 3-Installation enthalten:

from collections import Counter, defaultdict, namedtuple, deque, OrderedDict, ChainMap

Counter

Counter ist eine dict-Unterklasse, die zum Zählen hashbarer Objekte entwickelt wurde. Man übergibt ihr ein Iterable (oder einen string, oder Schlüsselwortargumente) und erhält ein wörterbuchähnliches Objekt zurück, bei dem die Schlüssel Elemente und die Werte deren Anzahl sind.

Counter erstellen

from collections import Counter

# From a list
word_list = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
c = Counter(word_list)
print(c)
# Output: Counter({'apple': 3, 'banana': 2, 'cherry': 1})

Fehlende Schlüssel geben 0 zurück, anstatt einen KeyError auszulösen:

print(c['apple'])   # 3
print(c['mango'])   # 0  — no KeyError

Häufigste Elemente

most_common(n) gibt die n Elemente mit der höchsten Anzahl als Liste von (element, count)-Tupeln zurück, sortiert von am häufigsten bis am seltensten:

print(c.most_common(2))
# Output: [('apple', 3), ('banana', 2)]

Lässt man n weg, werden alle Elemente nach Häufigkeit sortiert ausgegeben.

Counter-Arithmetik

Counter unterstützen Addition, Subtraktion, Schnittmenge und Vereinigung:

a = Counter(['a', 'a', 'b'])          # Counter({'a': 2, 'b': 1})
b = Counter(['a', 'b', 'b', 'c'])     # Counter({'b': 2, 'a': 1, 'c': 1})

print(a + b)   # Counter({'a': 3, 'b': 3, 'c': 1})
print(a - b)   # Counter({'a': 1})        — only positive counts kept
print(a & b)   # Counter({'a': 1, 'b': 1}) — minimum of each count
print(a | b)   # Counter({'a': 2, 'b': 2, 'c': 1}) — maximum of each count

Wann Counter verwenden: Stimmen auszählen, Worthäufigkeiten, Zeichenanzahl, Histogramm erstellen.

Counter-Tücke: Subtraktion behält nur positive Werte

a - b verwirft stillschweigend Elemente, bei denen das Ergebnis null oder negativ wäre. Wenn man negative Zählwerte beibehalten möchte, verwendet man stattdessen subtract():

a = Counter({'x': 2})
b = Counter({'x': 5})
a.subtract(b)
print(a)   # Counter({'x': -3})  — negative count preserved

defaultdict

defaultdict ist eine dict-Unterklasse, die eine Fabrikfunktion aufruft, um einen Standardwert bereitzustellen, wenn man einen noch nicht vorhandenen Schlüssel nachschlägt. Dadurch entfällt die Notwendigkeit von if key not in d:-Wächterklauseln.

defaultdict erstellen

Die Fabrik wird als erstes Argument übergeben:

from collections import defaultdict

dd = defaultdict(int)   # default value: int() == 0
words = ['cat', 'dog', 'cat', 'bird', 'dog', 'cat']
for word in words:
    dd[word] += 1       # no KeyError on first access

print(dict(dd))
# Output: {'cat': 3, 'dog': 2, 'bird': 1}

Ohne defaultdict würde man dd[word] = dd.get(word, 0) + 1 oder einen Counter benötigen.

Elemente mit list als Fabrik gruppieren

groups = defaultdict(list)
data = [('fruit', 'apple'), ('veggie', 'carrot'), ('fruit', 'banana'), ('veggie', 'broccoli')]
for category, item in data:
    groups[category].append(item)

print(dict(groups))
# Output: {'fruit': ['apple', 'banana'], 'veggie': ['carrot', 'broccoli']}

Häufige Fabrikfunktionen

FabrikStandardwertTypischer Einsatz
int0Zählen
float0.0Summen akkumulieren
list[]Elemente gruppieren
setset()Eindeutige Werte sammeln
str''Strings aufbauen
dict{}Verschachtelte Zuordnungen

Man kann auch ein nullstelliges Lambda für einen benutzerdefinierten Standard übergeben: defaultdict(lambda: 'N/A').

defaultdict-Tücke: Zugriff auf einen Schlüssel erstellt ihn

Anders als dict.get() fügt ein einfaches dd[key]-Nachschlagen bei einem fehlenden Schlüssel diesen Schlüssel mit dem Standardwert ein. Das kann beim Iterieren oder Prüfen der Zugehörigkeit überraschen:

dd = defaultdict(int)
print('foo' in dd)   # False — key does not exist yet
_ = dd['foo']        # access inserts the key
print('foo' in dd)   # True  — key was silently created

Verwendet man dd.get('foo') oder 'foo' in dd, wenn man ohne Nebeneffekte prüfen möchte.

Wann defaultdict verwenden: Daten gruppieren, Adjazenzlisten für Graphen erstellen, jedes Muster, bei dem man initialisiert und dann aktualisiert.


namedtuple

namedtuple erstellt eine neue Klasse, deren Instanzen wie reguläre Tupel sind, aber benannte Felder besitzen. Das Ergebnis ist unveränderlich, speichereffizient (kein __dict__ pro Instanz) und selbstdokumentierend.

namedtuple erstellen

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 7)

print(p)         # Point(x=3, y=7)
print(p.x)       # 3
print(p.y)       # 7
print(p[0])      # 3  — index access still works

Das erste Argument an namedtuple() ist der Typname (wird in repr verwendet). Das zweite Argument ist eine Liste von Feldnamen (oder ein durch Leerzeichen/Komma getrennter string: 'x y').

Praktisches Beispiel

Employee = namedtuple('Employee', ['name', 'department', 'salary'])
emp = Employee('Alice', 'Engineering', 95000)
print(emp.name, emp.department, emp.salary)
# Output: Alice Engineering 95000

Benannter Zugriff (emp.name) ist viel klarer als positionaler Zugriff (row[0]), wenn man Daten aus CSV-Dateien oder Datenbankzeilen liest.

Nützliche namedtuple-Methoden

# Convert to an ordered dictionary
print(p._asdict())        # {'x': 3, 'y': 7}

# Create a modified copy (namedtuples are immutable)
p2 = p._replace(x=10)
print(p2)                 # Point(x=10, y=7)
print(p)                  # Point(x=3, y=7)  — original unchanged

namedtuple vs. dataclass

Python 3.7 hat dataclasses.dataclass als Alternative eingeführt. Man wählt namedtuple, wenn man Unveränderlichkeit und volle Tupel-Kompatibilität (Entpacken, Indizieren, Hashing) möchte. Man wählt dataclass, wenn man veränderliche Felder, Standard-Fabriken oder Methoden benötigt.

Wann namedtuple verwenden: Datensätze repräsentieren (Datenbankzeilen, CSV-Zeilen, Koordinatenpaare, RGB-Farben), bei denen Unveränderlichkeit und geringer Speicherverbrauch wichtig sind.


deque

deque (doppelseitige Warteschlange, ausgesprochen „deck") ist eine Sequenz, die für O(1)-Anfüge- und Entfernoperationen an beiden Enden optimiert ist. Eine reguläre list erreicht O(1) bei append und O(n) bei insert(0, …); deque erreicht O(1) an beiden Enden.

deque erstellen

from collections import deque

d = deque([1, 2, 3])
print(d)   # deque([1, 2, 3])

Anfügen und Entfernen

d.append(4)        # add to right
d.appendleft(0)    # add to left
print(d)           # deque([0, 1, 2, 3, 4])

d.pop()            # remove from right  → 4
d.popleft()        # remove from left   → 0
print(d)           # deque([1, 2, 3])

Eine deque rotieren

rotate(n) verschiebt Elemente um n Positionen nach rechts (negativ = links):

d = deque([1, 2, 3])
d.rotate(1)
print(d)   # deque([3, 1, 2])

d.rotate(-1)
print(d)   # deque([1, 2, 3])

Begrenzte deque (Schiebefenster / FIFO-Puffer)

Das Setzen von maxlen begrenzt die Größe der deque. Wenn neue Elemente über die Grenze hinaus hinzugefügt werden, fallen Elemente automatisch vom gegenüberliegenden Ende heraus — ideal, um die letzten N Ereignisse zu speichern:

buffer = deque(maxlen=3)
for i in range(5):
    buffer.append(i)
print(buffer)   # deque([2, 3, 4], maxlen=3)

deque-Tücke: langsamer O(n)-Zufallszugriff

deque unterstützt keinen effizienten Zufallszugriff. d[500] ist O(n), nicht O(1) wie bei einer list. Wenn man häufig nach Position indiziert, sollte man eine list verwenden. deque nur verwenden, wenn man schnelle Hinzufüge- und Entfernoperationen an beiden Enden benötigt.

Wann deque verwenden: Warteschlangen und Stapel implementieren, Schiebefenster-Algorithmen, Breitensuche, die letzten N Log-Einträge speichern.


OrderedDict

Seit Python 3.7 behält ein reguläres dict die Einfügereihenfolge bei. Warum also OrderedDict verwenden?

Zwei Gründe bleiben relevant:

  1. move_to_end() — ermöglicht das effiziente Umordnen von Schlüsseln an den Anfang oder das Ende.
  2. Gleichheit — zwei OrderedDict-Instanzen mit denselben Schlüsseln in unterschiedlicher Einfügereihenfolge gelten als nicht gleich, im Gegensatz zu regulären dicts.

OrderedDict erstellen und umordnen

from collections import OrderedDict

od = OrderedDict()
od['one'] = 1
od['two'] = 2
od['three'] = 3
print(list(od.keys()))   # ['one', 'two', 'three']

od.move_to_end('one')          # move 'one' to the end
print(list(od.keys()))         # ['two', 'three', 'one']

od.move_to_end('three', last=False)  # move 'three' to the front
print(list(od.keys()))               # ['three', 'two', 'one']

Reihenfolgesensitive Gleichheit

od1 = OrderedDict([('a', 1), ('b', 2)])
od2 = OrderedDict([('b', 2), ('a', 1)])
print(od1 == od2)   # False — different order

d1 = {'a': 1, 'b': 2}
d2 = {'b': 2, 'a': 1}
print(d1 == d2)     # True  — regular dicts ignore order

Wann OrderedDict verwenden: LRU-Cache-Implementierungen (zuletzt verwendeten Schlüssel ans Ende verschieben), jeden Algorithmus, bei dem die Einfügereihenfolge Teil der Gleichheit sein muss.


ChainMap

ChainMap fasst mehrere Wörterbücher zu einer einzigen logischen Ansicht zusammen. Nachschläge durchsuchen die Maps der Reihe nach; Schreib- und Löschoperationen betreffen immer nur die erste Map.

Grundlegende Verwendung

from collections import ChainMap

defaults = {'color': 'blue', 'size': 'medium', 'theme': 'light'}
overrides = {'color': 'red', 'size': 'large'}

combined = ChainMap(overrides, defaults)
print(combined['color'])   # 'red'   — found in overrides first
print(combined['theme'])   # 'light' — not in overrides, falls back to defaults

Schreiboperationen gehen nur an die erste Map:

combined['font'] = 'serif'
print(overrides)   # {'color': 'red', 'size': 'large', 'font': 'serif'}
print(defaults)    # {'color': 'blue', 'size': 'medium', 'theme': 'light'} — unchanged

Variablen-Scopes mit new_child() simulieren

base = ChainMap({'x': 1})
child = base.new_child({'x': 99, 'y': 2})
print(child['x'])            # 99  — child scope shadows parent
print(child['y'])            # 2
print(child.parents['x'])    # 1   — access parent scope directly

new_child() gibt eine neue ChainMap mit einem leeren dict am Anfang zurück, was der internen Modellierung von Pythons eigenen Scoping-Regeln (lokal → umschließend → global → eingebaut) entspricht.

Wann ChainMap verwenden: Konfigurationsschichtung (Benutzer-Überschreibungen → Projektstandards → globale Standards), Umgebungen mit Gültigkeitsbereich implementieren, CLI-Argumente mit Umgebungsvariablen und Konfigurationsdateien kombinieren.


Den richtigen Typ wählen

Was man braucht…Verwende
Häufigkeit von Elementen zählenCounter
KeyError mit einem Standardwert vermeidendefaultdict
Einen Datensatz mit benannten Feldern repräsentierennamedtuple
Schnelles Anfügen/Entfernen an beiden Enden oder ein begrenzter Pufferdeque
Reihenfolgesensitive dict-Gleichheit oder move_to_end()OrderedDict
Mehrere dicts ohne Kopieren zu einer Ansicht zusammenführenChainMap

Mehr zu den Basistypen, die diese Typen erweitern, findet man unter Python Dictionaries, Python Lists und Python Tuples. Für iteratorbasierte Hilfsfunktionen in der Standardbibliothek siehe das Python itertools-Modul.

Übungen

Übung
Welcher collections-Typ gibt 0 zurück (anstatt KeyError auszulösen), wenn man auf einen fehlenden Schlüssel zugreift, und zählt Vorkommen automatisch?
Welcher collections-Typ gibt 0 zurück (anstatt KeyError auszulösen), wenn man auf einen fehlenden Schlüssel zugreift, und zählt Vorkommen automatisch?
Übung
Eine deque mit maxlen=3 enthält bereits [1, 2, 3]. Was enthält sie nach dem Aufruf von append(4)?
Eine deque mit maxlen=3 enthält bereits [1, 2, 3]. Was enthält sie nach dem Aufruf von append(4)?
Übung
Welche Aussage über OrderedDict ist in Python 3.7 und später wahr?
Welche Aussage über OrderedDict ist in Python 3.7 und später wahr?
Was this page helpful?