Datenstrukturen (Collections)
1 Listen
1.1 Zugriff auf Elemente, Teillisten
L = ['Null', 'Eins', 'Zwei', 'Drei']
# Anzahl der Elemente
print(len(L)) # 4
# Element 1
print(L[1]) # Eins
# Element 0-2
print(L[0:2]) # ['Null', 'Eins']
# Die letzten beiden Elemente
print(L[-2:]) # ['Zwei', 'Drei']
1.2 Elemente hinzufügen und löschen
L = [2, 4, 6, 8]
L.append(10) # Der Wert 10 wird hinzugefügt
print(L) # [2, 4, 6, 8, 10]
L.remove(4) # Der Wert 4 wird entfernt (NICHT Element 4)
print(L) # [2, 6, 8, 10]
del L[0:2] # Element 0-1 werden gelöscht, kein Rückgabewert
print(L) # [8, 10]
del L[1] # Element 1 wird gelöscht, kein Rückgabewert
print(L) # [8]
# Element 0 wird entfernt und zurückgegeben
print(L.pop(0)) # 8
1.3 Listen kombinieren
L1 = [1, 2, 3]
L2 = [4, 5, 6]
# Möglichkeit 1
print(L1 + L2) # [1, 2, 3, 4, 5, 6]
L1.extend(L2)
# Möglichkeit 2
print(L1) # [1, 2, 3, 4, 5, 6]
Prüfen, ob Wert in Liste ist:
L = ['A', 'B', 'C']
x = 'B'
print(x in L) # True
1.4 List comprehensions (kompakte Erstellung von Listen)
Eine List Comprehension ist eine kompakte Schreibweise zur Erstellung von Listen. Sie fasst eine for-Schleife – optional mit einer if-Bedingung – in einem einzigen Ausdruck zusammen.
Syntax:
[ausdruck for element in iterable]
[ausdruck for element in iterable if bedingung]
Beispiel 1:
numbers = []
for i in range(5):
numbers.append(i**2)
print(numbers) # [0, 1, 4, 9, 16]
# List comprehension:
numbers = [i**2 for i in range(5)]
print(numbers) # [0, 1, 4, 9, 16]
Beispiel 2:
L2 = []
for i in range(0, len(L)):
if L[i] > 1:
L2.append(L[i]*2)
print(L2) # [4, 6]
# List comprehension:
L = [1, 2, 3]
print([x*2 for x in L if x > 1]) # [4, 6]
1.4.1 Lesbarkeit von List Comprehensions
List Comprehensions sind in manchen Fällen besser lesbar, können jedoch auch schnell unübersichtlich werden!
Beispiel 1 (List Comprehension ist besser lesbar):
# Besser als List Comprehension – kompakt und klar:
squares = [x**2 for x in range(10)]
# Normale Schreibweise (umständlicher):
squares = []
for x in range(10):
squares.append(x**2)
Beispiel 2 (Normale Schreibweise ist verständlicher):
# Normale Schreibweise – übersichtlicher bei komplexer Logik:
results = []
for x in range(10):
value = x ** 2
if value % 3 == 0 and value > 5:
results.append(value * 2 - 1)
# List Comprehension (schwerer lesbar):
results = [x**2 * 2 - 1 for x in range(10) if x**2 % 3 == 0 and x**2 > 5]
Tip
Sobald die List Comprehension nicht mehr in eine einzelne Zeile passt oder mehrere verschachtelte Bedingungen enthält, ist die normale Schreibweise vorzuziehen.
1.4.2 List comprehensions mit if-Bedingungen
Beispiel 1:
# Beispiel 1
numbers = [i*-1 if i<3 else -1 for i in range(5)]
print(numbers) # [0, -1, -2, -1, -1]
# Beispiel 2
numbers = [i+1 for i in range(5) if i != 3]
print(numbers) # [1, 2, 3, 5]
Beim ersten Beispiel steht die if-Bedingung am Anfang, dadurch wird festgelegt, was abgespeichert wird. Beim zweiten Beispiel, bei der die if-Bedingung am Ende steht, wird festgelegt, ob überhaupt etwas abgespeichert werden soll.
1.5 Kopieren von Listen
danger
Das Kopieren einer Liste über Variablenzuweisen (z. B. l2 = l1) führt häufig zu einem nicht gewollten Verhalten, da das Ändern von l2 auch die Änderung von l1 mit sich bringt.
Beispiel:
l1 = [1, 2]
l2 = l1
l2[0] = 3
print(l1) # [3, 2]
print(l2) # [3, 2]
Dies liegt daran, dass auf diese Weite l1 und l2 auf dieselbe Speicheradresse verweisen:
l1 = [1, 2]
l2 = l1
print(id(l1)) # 1957190476864
print(id(l2)) # 1957190476864
Um das zu umgehen, kopiert man Listen wie folgt:
l1 = [1, 2]
l2 = l1.copy()
l2[0] = 3
print(l1) # [1, 2]
print(l2) # [3, 2]
warning
Dies ist eine sog. shallow-copy. Bei mehrdimensionalen Listen funktioniert dies jedoch nicht! Es muss eine sog. deep-copy erstellt werden.
Beispiel für shallow-copy:
import copy
l1 = [[1, 2], [3, 4]]
l2 = l1.copy() # oder l2 = copy.copy(l1)
l2[0][0] = 5
print(l1) # [[5, 2], [3, 4]]
print(l2) # [[5, 2], [3, 4]]
print(id(l1)) # 1957176932224
print(id(l2)) # 1957176994368
print(id(l1[0][0])) # 1956557750640
print(id(l2[0][0])) # 1956557750640
Beispiel für deep-copy:
import copy
l1 = [[1, 2], [3, 4]]
l2 = copy.deepcopy(l1)
l2[0][0] = 5
print(l1) # [[1, 2], [3, 4]]
print(l2) # [[5, 2], [3, 4]]
print(id(l1)) # 1957190472320
print(id(l2)) # 1957190477184
print(id(l1[0][0])) # 1956557750512
print(id(l2[0][0])) # 1956557750640
2 Arrays
Arrays sind ähnlich wie Listen, jedoch effizienter bei numerischen Operationen, insbesondere für große Datenmengen. In Python können Arrays mit dem array-Modul oder über NumPy verwendet werden.
import array
# Ein Array mit ganzen Zahlen (Typcode 'i' für Integer)
arr = array.array('i', [1, 2, 3, 4, 5])
print(arr) # array('i', [1, 2, 3, 4, 5])
# Elementzugriff
print(arr[1]) # 2
# Element hinzufügen
arr.append(6)
print(arr) # array('i', [1, 2, 3, 4, 5, 6])
# Element entfernen
arr.remove(3)
print(arr) # array('i', [1, 2, 4, 5, 6])
3 Tupel
Tupel sind wie Listen, jedoch mit dem Unterschied, dass der Inhalt des Tupels nicht veränderbar ist.
t = 1, 2, 3
t = (1, 2, 3)
print(t[0]) # 1
3.1 Tuple unpacking
Beim Tuple Unpacking werden die Elemente eines Tupels in einem einzigen Schritt mehreren Variablen zugewiesen. Mit _ als Platzhalter ignoriert man einzelne Werte, mit * fasst man mehrere verbleibende Elemente in einer Liste zusammen.
t = (1, 2, 3)
val1, val2, val3 = t
print(val1, val2, val3) # 1 2 3
val1, _, val3 = t
print(val1, val3) # 1 3
val1, *t2 = t
print(val1, t2) # 1 [2, 3]
4 Dictionaries
Dictionaries werden benutzt, um Daten in key-value-Paaren zu speichern.
Erstellung von Dictionaries:
# Möglichkeit 1:
year = {'Ford': 2019, 'Honda': 2013}
print(year) # {'Ford': 2019, 'Honda': 2013}
# Möglichkeit 2:
year = dict(Ford = 2019, Honda = 2013)
print(year) # {'Ford': 2019, 'Honda': 2013}
# Möglichkeit 3: Schlüssel werden als Tupel übergeben)
year = dict.fromkeys( ('Ford', 'Honda') )
year['Ford'] = 2019
year['Honda'] = 2013
print(year) # {'Ford': 2019, 'Honda': 2013}
Zugriff auf die Schlüssel und Werte von Dictionaries in Schleifen:
D = {'Ford': 2019, 'Honda': 2013}
print('Schlüssel des Dictionaries:')
for key in D:
print(key)
# Ford
# Honda
print('\nWerte des Dictionaries:')
for value in D.values():
print(value)
# 2019
# 2013
print('\nSchlüssel und Werte des Dictionaries:')
for key, value in D.items():
print(f'{key}: {value}')
# Ford: 2019
# Honda: 2013
note
Seit Python 3.7 ist die Einfügereihenfolge von Dictionaries garantiert. In älteren Python-Versionen war die Reihenfolge der Schlüssel nicht definiert.
5 Sets bzw. Mengen
Siehe auch operatoren.
Operationen:
S1 = {'Banane', 'Paprika', 'Zitrone'}
S2 = {'Apfel', 'Banane', 'Birne', 'Gurke', 'Paprika'}
# Vereinigung
print(S1 | S2) # {'Zitrone', 'Gurke', 'Birne', 'Paprika', 'Apfel', 'Banane'}
# Schnittmenge
print(S1 & S2) # {'Paprika', 'Banane'}
# Differenz (S1 ohne S2)
print(S1 - S2) # {'Zitrone'}
# Symmetrische Differenz (= Vereinigung ohne Schnittmenge)
print(S1 ^ S2) # {'Apfel', 'Zitrone', 'Gurke', 'Birne'}
print('\nAlternativen:')
# Vereinigung
print(S1.union(S2)) # {'Zitrone', 'Gurke', 'Birne', 'Paprika', 'Apfel', 'Banane'}
# Schnittmenge
print(S1.intersection(S2)) # {'Paprika', 'Banane'}
# Differenz (S1 ohne S2)
print(S1.difference(S2)) # {'Zitrone'}
# Symmetrische Differenz (= Vereinigung ohne Schnittmenge)
print(S1.symmetric_difference(S2)) # {'Apfel', 'Zitrone', 'Gurke', 'Birne'}
Mehr zu Datentypen: https://docs.python.org/3/library/stdtypes.html
6 Das collections-Modul
Die eingebauten Datenstrukturen list, dict, tuple und set decken die meisten Anwendungsfälle ab. Das collections-Modul ergänzt sie um spezialisierte Container, die für bestimmte Aufgaben effizienter oder ausdrucksstärker sind und häufig wiederkehrenden Boilerplate-Code vermeiden.
6.1 Counter – Zählen von Elementen
Counter ist ein Dictionary-Subtyp zum Zählen von hashbaren Objekten.
6.1.1 Grundlegende Verwendung
from collections import Counter
# Aus Liste erstellen
words = ['Apfel', 'Banane', 'Apfel', 'Tomate', 'Banane', 'Apfel']
counter = Counter(words)
print(counter) # Counter({'Apfel': 3, 'Banane': 2, 'Tomate': 1})
# Aus String erstellen (zählt Zeichen)
text = 'mississippi'
counter = Counter(text)
print(counter) # Counter({'i': 4, 's': 4, 'p': 2, 'm': 1})
# Direkte Initialisierung
counter = Counter(a=3, b=2, c=1)
print(counter) # Counter({'a': 3, 'b': 2, 'c': 1})
6.1.2 Häufigste Elemente
from collections import Counter
votes = ['Anna', 'Bernd', 'Anna', 'Christian', 'Bernd', 'Anna']
counter = Counter(votes)
# Häufigste n Elemente
print(counter.most_common(2)) # [('Anna', 3), ('Bernd', 2)]
# Alle Elemente nach Häufigkeit
print(counter.most_common()) # [('Anna', 3), ('Bernd', 2), ('Christian', 1)]
6.1.3 Counter-Operationen
from collections import Counter
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
# Addition
print(c1 + c2) # Counter({'a': 4, 'b': 3})
# Subtraktion (negative Werte werden entfernt)
print(c1 - c2) # Counter({'a': 2})
# Vereinigung (Maximum)
print(c1 | c2) # Counter({'a': 3, 'b': 2})
# Schnittmenge (Minimum)
print(c1 & c2) # Counter({'a': 1, 'b': 1})
6.1.4 Praktische Beispiele
Wortfrequenz-Analyse:
from collections import Counter
text = """Python ist eine wunderbare Programmiersprache.
Python macht Spaß und Python ist mächtig."""
words = text.lower().split()
word_count = Counter(words)
print(word_count.most_common(3))
# [('python', 3), ('ist', 2), ('eine', 1)]
Duplikate finden:
from collections import Counter
data = [1, 2, 3, 2, 4, 5, 3, 6, 7, 3]
counter = Counter(data)
# Elemente, die mehr als einmal vorkommen
duplicates = [item for item, count in counter.items() if count > 1]
print(duplicates) # [2, 3]
6.2 defaultdict – Dictionary mit Standardwerten
defaultdict erstellt automatisch Standardwerte für fehlende Schlüssel.
6.2.1 Grundkonzept
from collections import defaultdict
# Mit list als Factory
d = defaultdict(list)
d['obst'].append('Apfel')
d['obst'].append('Banane')
d['gemüse'].append('Karotte')
print(d) # defaultdict(<class 'list'>, {'obst': ['Apfel', 'Banane'], 'gemüse': ['Karotte']})
# Mit int als Factory (Standardwert 0)
counter = defaultdict(int)
for word in ['a', 'b', 'a', 'c', 'b', 'a']:
counter[word] += 1
print(counter) # defaultdict(<class 'int'>, {'a': 3, 'b': 2, 'c': 1})
6.2.2 Verschiedene Factory-Funktionen
from collections import defaultdict
# list - leere Liste
d1 = defaultdict(list)
print(d1['key']) # []
# int - 0
d2 = defaultdict(int)
print(d2['key']) # 0
# str - leerer String
d3 = defaultdict(str)
print(d3['key']) # ''
# set - leeres Set
d4 = defaultdict(set)
print(d4['key']) # set()
# Lambda für custom Defaults
d5 = defaultdict(lambda: 'N/A')
print(d5['key']) # 'N/A'
6.2.3 Vergleich: dict vs. defaultdict
# Normales dict
d = {}
d['key'].append('value') # KeyError!
# Mit setdefault (umständlich)
d.setdefault('key', []).append('value')
# Mit defaultdict (elegant)
from collections import defaultdict
d = defaultdict(list)
d['key'].append('value') # Funktioniert!
6.2.4 Praktische Beispiele
Gruppieren nach Schlüssel:
from collections import defaultdict
students = [
('Anna', 'Mathe'),
('Bernd', 'Physik'),
('Christian', 'Mathe'),
('David', 'Physik'),
('Erwin', 'Mathe')
]
by_subject = defaultdict(list)
for name, subject in students:
by_subject[subject].append(name)
print(dict(by_subject))
# {'Mathe': ['Anna', 'Christian', 'Erwin'], 'Physik': ['Bernd', 'David']}
Verschachtelte defaultdict:
from collections import defaultdict
# Zweistufiges defaultdict
tree = lambda: defaultdict(tree)
users = tree()
users['Bernd']['age'] = 30
users['Bernd']['city'] = 'New York'
users['Anna']['age'] = 25
print(dict(users))
# {'Bernd': {'age': 30, 'city': 'New York'}, 'Anna': {'age': 25}}
6.3 deque – Double-Ended Queue
Im Gegensatz zur normalen Liste, bei der das Einfügen oder Entfernen am Anfang alle übrigen Elemente verschiebt (O(n)), bietet deque für beide Enden konstante Zeit (O(1)). Sie eignet sich daher besonders für Queues, Stacks und Sliding-Window-Algorithmen, bei denen häufig Elemente vorne oder hinten eingefügt bzw. entfernt werden.
6.3.1 Grundoperationen
from collections import deque
# Erstellen
d = deque([1, 2, 3])
print(d) # deque([1, 2, 3])
# Am Ende hinzufügen/entfernen (wie Liste)
d.append(4)
print(d) # deque([1, 2, 3, 4])
last = d.pop()
print(last, d) # 4 deque([1, 2, 3])
# Am Anfang hinzufügen/entfernen (O(1) statt O(n))
d.appendleft(0)
print(d) # deque([0, 1, 2, 3])
first = d.popleft()
print(first, d) # 0 deque([1, 2, 3])
6.3.2 Rotation und Erweiterung
from collections import deque
d = deque([1, 2, 3, 4, 5])
# Rotation nach rechts
d.rotate(2)
print(d) # deque([4, 5, 1, 2, 3])
# Rotation nach links
d.rotate(-2)
print(d) # deque([1, 2, 3, 4, 5])
# Mehrere Elemente hinzufügen
d.extend([6, 7])
print(d) # deque([1, 2, 3, 4, 5, 6, 7])
d.extendleft([0, -1])
print(d) # deque([-1, 0, 1, 2, 3, 4, 5, 6, 7])
6.3.3 Maximale Länge (Ringbuffer)
from collections import deque
# Deque mit maximaler Länge
d = deque(maxlen=3)
d.append(1)
d.append(2)
d.append(3)
print(d) # deque([1, 2, 3], maxlen=3)
d.append(4) # Ältestes Element (1) wird automatisch entfernt
print(d) # deque([2, 3, 4], maxlen=3)
6.3.4 Performance-Vergleich: list vs. deque
from collections import deque
import time
# Liste
data_list = []
start = time.time()
for i in range(100000):
data_list.insert(0, i) # O(n) - langsam!
print(f'List insert(0): {time.time() - start:.3f}s')
# Deque
data_deque = deque()
start = time.time()
for i in range(100000):
data_deque.appendleft(i) # O(1) - schnell!
print(f'Deque appendleft: {time.time() - start:.3f}s')
Typisches Ergebnis:
- List insert(0): 3.5 s
- Deque appendleft: 0.01 s
6.3.5 Praktische Beispiele
FIFO-Queue (First In, First Out):
from collections import deque
queue = deque()
# Elemente hinzufügen
queue.append('Aufgabe 1')
queue.append('Aufgabe 2')
queue.append('Aufgabe 3')
# Elemente verarbeiten (FIFO)
while queue:
task = queue.popleft()
print(f'Verarbeite: {task}')
Sliding Window:
from collections import deque
def moving_average(values, window_size):
"""Berechnet gleitenden Durchschnitt"""
window = deque(maxlen=window_size)
averages = []
for value in values:
window.append(value)
averages.append(sum(window) / len(window))
return averages
data = [10, 20, 30, 40, 50, 60]
result = moving_average(data, window_size=3)
print(result) # [10.0, 15.0, 20.0, 30.0, 40.0, 50.0]
**Browser History (Back/Forward): **
from collections import deque
class BrowserHistory:
def __init__(self):
self.back_stack = deque()
self.forward_stack = deque()
self.current = None
def visit(self, url):
if self.current:
self.back_stack.append(self.current)
self.current = url
self.forward_stack.clear()
def back(self):
if self.back_stack:
self.forward_stack.append(self.current)
self.current = self.back_stack.pop()
return self.current
def forward(self):
if self.forward_stack:
self.back_stack.append(self.current)
self.current = self.forward_stack.pop()
return self.current
# Verwendung
browser = BrowserHistory()
browser.visit('google.com')
browser.visit('python.org')
browser.visit('github.com')
print(browser.back()) # python.org
print(browser.back()) # google.com
print(browser.forward()) # python.org
6.4 ChainMap – Mehrere Dictionaries verketten
ChainMap gruppiert mehrere Dictionaries zu einem einzigen View.
6.4.1 Grundkonzept
from collections import ChainMap
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict3 = {'c': 5, 'd': 6}
# ChainMap erstellen
chain = ChainMap(dict1, dict2, dict3)
print(chain['a']) # 1 (aus dict1)
print(chain['b']) # 2 (aus dict1, nicht dict2!)
print(chain['c']) # 4 (aus dict2, nicht dict3!)
print(chain['d']) # 6 (aus dict3)
important
Bei Lookup wird das erste Dictionary mit dem Schlüssel verwendet.
6.4.2 Modifikationen
from collections import ChainMap
user_config = {'theme': 'dark', 'font_size': 12}
default_config = {'theme': 'light', 'font_size': 14, 'auto_save': True}
config = ChainMap(user_config, default_config)
print(config['theme']) # 'dark' (aus user_config)
print(config['auto_save']) # True (aus default_config)
# Änderungen gehen ins ERSTE Dictionary
config['theme'] = 'blue'
print(user_config) # {'theme': 'blue', 'font_size': 12}
print(default_config) # Unverändert
# Neuer Schlüssel wird auch ins ERSTE Dictionary eingefügt
config['new_key'] = 'value'
print(user_config) # {'theme': 'blue', 'font_size': 12, 'new_key': 'value'}
6.4.3 Methoden
from collections import ChainMap
dict1 = {'a': 1}
dict2 = {'b': 2}
chain = ChainMap(dict1, dict2)
# Neues Dictionary vorne hinzufügen
chain = chain.new_child({'c': 3})
print(chain) # ChainMap({'c': 3}, {'a': 1}, {'b': 2})
# Erstes Dictionary entfernen
chain = chain.parents
print(chain) # ChainMap({'a': 1}, {'b': 2})
# Alle Maps anzeigen
print(chain.maps) # [{'a': 1}, {'b': 2}]
6.4.4 Praktische Beispiele
Konfiguration mit Fallbacks:
from collections import ChainMap
import os
# Priorität: CLI-Args > Env-Vars > Config-File > Defaults
defaults = {
'host': 'localhost',
'port': 8000,
'debug': False
}
config_file = {
'host': '0.0.0.0',
'port': 3000
}
env_vars = {
k.lower().replace('app_', ''): v
for k, v in os.environ.items()
if k.startswith('APP_')
}
cli_args = {
'debug': True
}
config = ChainMap(cli_args, env_vars, config_file, defaults)
print(config['host']) # '0.0.0.0' (aus config_file)
print(config['debug']) # True (aus cli_args)
print(config['port']) # 3000 (aus config_file)
Scope-Management (z.B. für Interpreter):
from collections import ChainMap
class Scope:
def __init__(self):
self.scopes = ChainMap({})
def push_scope(self):
"""Neuer Scope (z.B. neue Funktion)"""
self.scopes = self.scopes.new_child()
def pop_scope(self):
"""Scope verlassen"""
self.scopes = self.scopes.parents
def set(self, name, value):
"""Variable im aktuellen Scope setzen"""
self.scopes[name] = value
def get(self, name):
"""Variable nachschlagen (mit Fallback zu äußeren Scopes)"""
return self.scopes.get(name)
# Verwendung
scope = Scope()
scope.set('x', 10) # Global: x=10
scope.push_scope() # Neue Funktion
scope.set('x', 20) # Lokal: x=20
print(scope.get('x')) # 20
scope.pop_scope() # Funktion verlassen
print(scope.get('x')) # 10 (global wieder sichtbar)
6.5 namedtuple – Tupel mit benannten Feldern
namedtuple erstellt Tuple-Subklassen mit benannten Feldern.
6.5.1 Grundlegende Verwendung
from collections import namedtuple
# namedtuple-Klasse definieren
Point = namedtuple('Point', ['x', 'y'])
# Instanzen erstellen
p1 = Point(10, 20)
p2 = Point(x=30, y=40)
# Zugriff per Name oder Index
print(p1.x) # 10
print(p1[0]) # 10
print(p1.y) # 20
# Tuple unpacking funktioniert
x, y = p1
print(x, y) # 10 20
6.5.2 Verschiedene Erstellungsmethoden
from collections import namedtuple
# Methode 1: Liste von Strings
Point = namedtuple('Point', ['x', 'y'])
# Methode 2: String mit Leerzeichen
Point = namedtuple('Point', 'x y')
# Methode 3: String mit Kommas
Point = namedtuple('Point', 'x, y')
6.5.3 Methoden von namedtuple
from collections import namedtuple
Point = namedtuple('Point', 'x y')
p = Point(10, 20)
# Als Dictionary
print(p._asdict()) # {'x': 10, 'y': 20}
# Felder anzeigen
print(p._fields) # ('x', 'y')
# Neues Objekt mit geänderten Werten
p2 = p._replace(x=30)
print(p2) # Point(x=30, y=20)
# Aus iterable erstellen
Point._make([40, 50]) # Point(x=40, y=50)
6.5.4 Default-Werte
from collections import namedtuple
# Mit defaults (Python 3.7+)
Point = namedtuple('Point', ['x', 'y', 'z'], defaults=[0, 0, 0])
p1 = Point()
print(p1) # Point(x=0, y=0, z=0)
p2 = Point(10)
print(p2) # Point(x=10, y=0, z=0)
p3 = Point(10, 20)
print(p3) # Point(x=10, y=20, z=0)
6.5.5 Vergleich: namedtuple vs. dict vs. class
from collections import namedtuple
# namedtuple
Person = namedtuple('Person', 'name age')
p1 = Person('Anna', 30)
# Dictionary
p2 = {'name': 'Anna', 'age': 30}
# Klasse
class PersonClass:
def __init__(self, name, age):
self.name = name
self.age = age
p3 = PersonClass('Anna', 30)
# Speicherverbrauch
import sys
print(f'namedtuple: {sys.getsizeof(p1)} bytes') # ~56
print(f'dict: {sys.getsizeof(p2)} bytes') # ~232
print(f'class: {sys.getsizeof(p3)} bytes') # ~48 + __dict__
6.5.6 Praktische Beispiele
CSV-Daten verarbeiten:
from collections import namedtuple
import csv
# CSV-Daten
csv_data = """Name,Alter,Stadt
Anna,30,Flensburg
Bernd,25,Hamburg
Christian,35,Kiel"""
# namedtuple aus CSV-Header
lines = csv_data.strip().split('\n')
header = lines[0].split(',')
Person = namedtuple('Person', header)
# Daten parsen
people = []
for line in lines[1:]:
values = line.split(',')
person = Person(*values)
people.append(person)
for p in people:
print(f'{p.name} ist {p.age} Jahre alt und aus {p.city}')
Koordinaten-System:
from collections import namedtuple
import math
Point = namedtuple('Point', 'x y')
def distance(p1, p2):
"""Euklidische Distanz zwischen zwei Punkten"""
return math.sqrt((p2.x - p1.x)**2 + (p2.y - p1.y)**2)
p1 = Point(0, 0)
p2 = Point(3, 4)
print(f'Distanz: {distance(p1, p2)}') # 5.0
RGB-Farben:
from collections import namedtuple
Color = namedtuple('Color', 'red green blue')
# Vordefinierte Farben
BLACK = Color(0, 0, 0)
WHITE = Color(255, 255, 255)
RED = Color(255, 0, 0)
def to_hex(color):
return f'#{color.red:02x}{color.green:02x}{color.blue:02x}'
print(to_hex(RED)) # #ff0000
6.6 Zusammenfassung
| Collection | Verwendung | Vorteil |
|---|---|---|
Counter | Elemente zählen | Einfache Häufigkeitsanalyse |
defaultdict | Dictionary mit Auto-Initialisierung | Kein KeyError, weniger Code |
deque | Queue/Stack mit Zugriff an beiden Enden | O(1) append/pop an beiden Enden |
ChainMap | Mehrere Dictionaries verketten | Konfiguration mit Fallbacks |
namedtuple | Tupel mit benannten Feldern | Lesbar, leichtgewichtig |
Das collections-Modul bietet spezialisierte Datenstrukturen für häufige Anwendungsfälle, die effizienter und lesbarer sind, als selbstgebaute Lösungen.