Kontrollstrukturen
1 Bedingungen
1.1 if, elif und else
a = 1
b = 2
if a > b:
print('a ist größer als b')
elif a < b: # = elseif bzw. else if in anderen Sprachen
print('a ist kleiner als b')
else:
print('a ist gleich b')
Häufig kann man direkte Vergleiche weglassen:
check = True
string = 'Text'
if check is True:
print('Bedingung erfüllt.')
if len(string) != 0:
print('String ist nicht leer.')
# Kürzer:
if check:
print('Bedingung erfüllt.')
if string:
print('String ist nicht leer.')
1.2 Shorthand
a = 1
b = 2
if a < b: print('a ist kleiner als b')
1.3 Ternary operators (conditional expressions)
Ein Ternary Operator (conditional expression) fasst eine einfache if-else-Abfrage in einer einzigen Zeile zusammen. Die Syntax lautet: wert_wenn_wahr if bedingung else wert_wenn_falsch. Das Ergebnis wird direkt einer Variable zugewiesen oder weiterverwendet. Sinnvoll ist diese Schreibweise, wenn der Ausdruck kurz und gut lesbar bleibt.
a = 1
b = 2
comment = 'a ist größer als b' if a > b else 'a ist kleiner als b'
print(comment) # a ist kleiner als b
1.4 ShortHand Ternary
Python wertet logische Ausdrücke mit or lazy aus: Der rechte Operand wird nur dann betrachtet, wenn der linke falsy ist (also False, None, 0, '', [] usw.). Das lässt sich als kompakte Alternative zu einem Ternary Operator nutzen, um Fallback-Werte zu setzen.
Beispiel 1:
print( True or 'Some' ) # True
Beispiel 2:
print( False or 'Some' ) # Some
Beispiel 3:
output = None
msg = output or 'Keine Daten'
print(msg) # Keine Daten
1.5 Verkettete Vergleiche
Python erlaubt mathematisch natürliche Vergleichsketten, die von links nach rechts ausgewertet werden:
x = 1.5
# Andere Sprachen:
if x > 1 and x < 2:
print('x liegt zwischen 1 und 2')
# Python-Kurzform:
if 1 < x < 2:
print('x liegt zwischen 1 und 2')
2 Switch-Case-Äquivalent für Python-Version < 3.10
Vor Python-Version 3.10 gibt es keine Switch-Case-Anweisung, sie muss simuliert werden:
def switcher(age):
if age < 18:
msg = 'Minderjährig'
elif age < 67:
msg = 'Volljährig'
elif age in range(0, 150):
msg = 'Rentner'
else:
msg = 'Fehler'
return msg
print(switcher(50)) # Volljährig
3 Pattern Matching mit match-case (Python 3.10+)
Seit Python 3.10 gibt es strukturelles Pattern Matching mit match-case. Dies ist deutlich mächtiger als einfache Switch-Case-Statements aus anderen Sprachen.
3.1 Einfaches Matching (Literal Patterns)
def http_status(status):
match status:
case 200:
return 'OK'
case 404:
return 'Nicht gefunden'
case 500:
return 'Interner Serverfehler'
case _: # Wildcard (Standard)
return 'Unbekannter Status'
print(http_status(404)) # Nicht gefunden
3.2 Mehrere Werte (OR-Patterns)
def classify_http_status(status):
match status:
case 200 | 201 | 204:
return 'Erfolgreich'
case 400 | 401 | 403 | 404:
return 'Client-Fehler'
case 500 | 502 | 503:
return 'Server-Fehler'
case _:
return 'Unbekannt'
print(classify_http_status(201)) # Erfolgreich
3.3 Guards (if-Bedingungen)
age = 50
match age:
case _ if age < 0:
msg = 'Ungültiges Alter'
case _ if age < 18:
msg = 'Minderjähriger'
case _ if age < 67:
msg = 'Erwachsener'
case _ if age < 150:
msg = 'Rentner'
case _:
msg = 'Ungültiges Alter'
print(msg) # Erwachsener
3.4 Sequence Patterns (Listen, Tupel)
# Listen/Tupel matchen
point = (0, 5)
match point:
case (0, 0):
print('Ursprung')
case (0, y):
print(f'Auf y-Achse bei y={y}')
case (x, 0):
print(f'Auf x-Achse bei x={x}')
case (x, y):
print(f'Punkt auf ({x}, {y})')
# Ausgabe: Aus y-Achse bei y=5
Variable-length Patterns:
data = [1, 2, 3, 4, 5]
match data:
case []:
print('Leere Liste')
case [x]:
print(f'Einzelnes Element: {x}')
case [x, y]:
print(f'Zwei Elemente: {x}, {y}')
case [first, *rest]:
print(f'Erstes: {first}, Rest: {rest}')
# Ausgabe: Erstes: 1, Rest: [2, 3, 4, 5]
Exakte Länge mit Wildcard:
coordinates = (10, 20, 30)
match coordinates:
case (x, y):
print(f'2D: {x}, {y}')
case (x, y, z):
print(f'3D: {x}, {y}, {z}')
case (x, y, z, _):
print(f'4D+: Erste drei {x}, {y}, {z}')
# Ausgabe: 3D: 10, 20, 30
3.5 Mapping Patterns (Dictionaries)
user = {'name': 'Alice', 'age': 30, 'role': 'admin'}
match user:
case {'role': 'admin', 'name': name}:
print(f'Administrator: {name}')
case {'role': 'user', 'name': name}:
print(f'Normaler Benutzer: {name}')
case {'name': name}:
print(f'Benutzer ohne Rolle: {name}')
# Ausgabe: Administrator: Alice
Wichtig: Dictionaries matchen partial (zusätzliche Keys werden ignoriert):
data = {'type': 'point', 'x': 10, 'y': 20, 'color': 'red'}
match data:
case {'type': 'point', 'x': x, 'y': y}:
print(f'Punkt bei ({x}, {y})')
# 'color' wird ignoriert
# Ausgabe: Punkt bei (10, 20)
3.6 Class Patterns (Strukturelles Matching)
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
@dataclass
class Circle:
center: Point
radius: int
shape = Circle(Point(0, 0), 5)
match shape:
case Circle(center=Point(x=0, y=0), radius=r):
print(f'Kreis im Ursprung mit Radius {r}')
case Circle(center=Point(x=x, y=y), radius=r):
print(f'Kreis bei ({x}, {y}) mit Radius {r}')
# Ausgabe: Kreis im Ursprung mit Radius 5
Kürzere Syntax:
match shape:
case Circle(Point(0, 0), r):
print(f'Kreis im Ursprung mit Radius {r}')
case Circle(Point(x, y), r):
print(f'Kreis bei ({x}, {y}) mit Radius {r}')
3.7 AS Patterns (Capture Whole + Parts)
Mit as kann man sowohl das Gesamtobjekt als auch Teile davon erfassen:
data = [1, 2, 3, 4]
match data:
case [x, *rest] as full_list:
print(f'Erstes: {x}')
print(f'Rest: {rest}')
print(f'Gesamte Liste: {full_list}')
# Ausgabe:
# Erstes: 1
# Rest: [2, 3, 4]
# Gesamte Liste: [1, 2, 3, 4]
3.8 Verschachtelte Patterns
command = ('move', {'x': 10, 'y': 20})
match command:
case ('move', {'x': x, 'y': y}):
print(f'Bewege zu ({x}, {y})')
case ('resize', {'width': w, 'height': h}):
print(f'Skaliere auf {w}x{h}')
case ('rotate', {'angle': angle}):
print(f'Drehe um {angle}°')
# Ausgabe: Bewege zu (10, 20)
3.9 Best Practices
✅ DO:
- Nutze Pattern Matching für komplexe Datenstrukturen
- Verwende Guards für zusätzliche Bedingungen
- Ordne Patterns von spezifisch zu allgemein
- Nutze
_als Wildcard/Default-Case
❌ DON’T:
- Verwende nicht
matchfür einfacheif-elif-Ketten - Vermeide zu komplexe, verschachtelte Patterns
3.10 Pattern Matching vs. if-elif
Wann match verwenden:
# Gut für strukturelle Daten
match point:
case (0, 0): return 'Ursprung'
case (x, 0): return f'x-Achse: {x}'
case (0, y): return f'y-Achse: {y}'
case (x, y): return f'Punkt: ({x}, {y})'
Wann if-elif verwenden:
# Besser für einfache Vergleiche
if age < 18:
return 'Minderjährig'
elif age < 65:
return 'Erwachsen'
else:
return 'Senior'
3.11 Zusammenfassung
| Pattern-Typ | Beispiel | Verwendung |
|---|---|---|
| Literal | case 200: | Exakter Wert |
| Wildcard | case _: | Match alles (default) |
| Capture | case x: | Wert in Variable speichern |
| OR | case 200 | 201 | 204: | Mehrere Werte |
| Sequence | case [x, y, z]: | Listen/Tupel mit fester Länge |
| Sequence (rest) | case [first, *rest]: | Variable Länge |
| Mapping | case {'key': value}: | Dictionaries |
| Class | case Point(x, y): | Objekte/Dataclasses |
| Guard | case x if x > 0: | Zusätzliche Bedingung |
| AS | case [x, *rest] as full: | Ganzes + Teile erfassen |
4 Schleifen
4.1 for-Schleifen
L = [0, 1, 2, 3]
count = len(L)
# von 0 bis count-1
for i in range(count):
print(L[i], end=', ' if i < count-1 else '\n') # 0, 1, 2, 3
# von 1 bis count-1
for i in range(1, count):
print(L[i], end=', ' if i < count-1 else '\n') # 1, 2, 3
# von 0 bis count-1 mit Schrittweite 2
for i in range(0, count, 2):
print(L[i], end=', ' if i < count-2 else '\n') # 0, 2
# von count-1 bis 0 (rückwärts)
for i in range(count-1, -1, -1):
print(L[i], end=', ' if i != 0 else '\n') # 3, 2, 1, 0
D = {'A': 1, 'B': 3, 'C': 7}
for key in D:
print(key, end=' ') # A, B, C
print('')
for key, value in D.items():
print(f'{key}={value}', end=' ') # A=1 B=3 C=7
Mit Zählervariable:
lst = ['A', 'B', 'C']
for i in range(len(lst)):
print(lst[i])
# Kürzer:
for value in lst:
print(value)
# Wird der Index benötigt:
for i, value in enumerate(lst):
print(f'{i}: {value}')
Bildung eines Iterators mittels zip():
a = [1, 2, 3]
b = [4, 5, 6]
for i in range(len(a)):
print(f'{a[i]} und {b[i]}')
# Besser:
for av, bv in zip(a, b):
print(f'{av} und {bv}')
# Mit Index:
for i, (av, bv) in enumerate(zip(a, b)):
print(f'{i}: {av} und {bv}')
4.2 while-Schleifen
Mit Zählbedingung:
i = 0
m = 3
while i <= m:
print(i)
i += 1
Mit Flag:
i = 0
m = 3
run = True
while run:
print(i)
if i == m: run = False
i += 1
Endlosschleife mit break:
i = 0
m = 3
while True:
print(i)
if i == m: break
i += 1
4.3 for-else und while-else
Der else-Block einer Schleife wird ausgeführt, wenn die Schleife ohne break beendet wurde, d. h. vollständig durchgelaufen ist.
# Suche nach einem Element
values = [1, 3, 5, 7, 9]
for v in values:
if v == 4:
print('Gefunden!')
break
else:
print('Nicht gefunden.') # Wird ausgeführt, da kein break
# Ausgabe: Nicht gefunden.
Typischer Anwendungsfall: Prüfen, ob eine Bedingung in einer Schleife jemals erfüllt wurde, ohne eine Extra-Flag-Variable.
# Mit Flag (umständlicher):
found = False
for v in values:
if v % 2 == 0:
found = True
break
if not found:
print('Keine geraden Zahlen.')
# Kürzer mit for-else:
for v in values:
if v % 2 == 0:
break
else:
print('Keine geraden Zahlen.')
Counter aus collections
Counter zählt Häufigkeiten von Elementen in einem Iterable und eignet sich gut als Alternative zu manuellen Zählschleifen.
from collections import Counter
words = ['apfel', 'birne', 'apfel', 'kirsche', 'birne', 'apfel']
# Manuell (umständlich):
counts = {}
for w in words:
counts[w] = counts.get(w, 0) + 1
# Mit Counter:
counts = Counter(words)
print(counts)
# Counter({'apfel': 3, 'birne': 2, 'kirsche': 1})
# Häufigste Elemente
print(counts.most_common(2)) # [('apfel', 3), ('birne', 2)]
# Einzelne Abfrage
print(counts['apfel']) # 3
print(counts['zitrone']) # 0 (kein KeyError!)
Counter lässt sich auch mit anderen Counter-Objekten addieren oder subtrahieren:
a = Counter(['x', 'y', 'x'])
b = Counter(['y', 'y', 'z'])
print(a + b) # Counter({'y': 3, 'x': 2, 'z': 1})
print(a - b) # Counter({'x': 2}) (nur positive Werte)