it-praxisportal/12_exceptions_und_fehlerbehandlung/musterloesung.py

141 lines
5.3 KiB
Python

"""
Kapitel 12: Exceptions & Fehlerbehandlung 🛡️🛑
=============================================
Musterlösung für die Bankkonto- und Geldautomat-Simulation.
"""
from typing import Optional
# ==============================================================================
# 1. Eigene Exception-Hierarchie
# ==============================================================================
class BankFehler(Exception):
"""Basisklasse für alle Fehler im Bankkonto-System."""
pass
class UngueltigePinError(BankFehler):
"""Wird geworfen, wenn die eingegebene PIN falsch ist."""
pass
class KontoGesperrtError(BankFehler):
"""Wird geworfen, wenn das Konto gesperrt ist (oder nach 3 Fehlversuchen gesperrt wird)."""
pass
class NichtGenugGuthabenError(BankFehler):
"""Wird geworfen, wenn das Guthaben für eine Abhebung nicht ausreicht."""
pass
class UngueltigerBetragError(BankFehler):
"""Wird geworfen, wenn ein ungültiger (z.B. <= 0) Betrag übergeben wird."""
pass
# ==============================================================================
# 2. Klasse Bankkonto
# ==============================================================================
class Bankkonto:
def __init__(self, inhaber: str, pin: str, kontostand: float = 0.0):
"""Initialisiert ein Bankkonto mit Inhaber, PIN und Startguthaben."""
if kontostand < 0:
raise UngueltigerBetragError("Der Anfangskontostand darf nicht negativ sein!")
self.inhaber: str = inhaber
self.pin: str = pin
self.kontostand: float = float(kontostand)
self.gesperrt: bool = False
self.fehlversuche: int = 0
def pin_pruefen(self, eingegebene_pin: str) -> bool:
"""Prüft die PIN, zählt Fehlversuche und sperrt ggf. das Konto."""
if self.gesperrt:
raise KontoGesperrtError(f"Das Konto von {self.inhaber} ist gesperrt!")
if eingegebene_pin == self.pin:
self.fehlversuche = 0
return True
else:
self.fehlversuche += 1
if self.fehlversuche >= 3:
self.gesperrt = True
raise KontoGesperrtError("PIN 3-mal falsch eingegeben! Das Konto wurde aus Sicherheitsgründen gesperrt.")
raise UngueltigePinError(f"Falsche PIN! Verbleibende Versuche: {3 - self.fehlversuche}")
def einzahlen(self, betrag: float) -> float:
"""Zahlt Geld auf das Konto ein."""
if self.gesperrt:
raise KontoGesperrtError(f"Das Konto von {self.inhaber} ist gesperrt!")
if betrag <= 0:
raise UngueltigerBetragError("Einzahlungsbetrag muss größer als 0 sein!")
self.kontostand += betrag
return self.kontostand
def abheben(self, betrag: float, pin: str) -> float:
"""Hebt Geld nach erfolgreicher PIN- und Guthabenprüfung ab."""
# 1. PIN & Sperre prüfen (wirft automatisch passende Fehler)
self.pin_pruefen(pin)
# 2. Betrag prüfen
if betrag <= 0:
raise UngueltigerBetragError("Abhebebetrag muss größer als 0 sein!")
# 3. Deckung prüfen
if betrag > self.kontostand:
raise NichtGenugGuthabenError(f"Nicht genug Guthaben! Verfügbar: {self.kontostand:.2f}€, Angefordert: {betrag:.2f}€")
self.kontostand -= betrag
return self.kontostand
def sperren(self) -> None:
"""Sperrt das Konto manuell."""
self.gesperrt = True
def entsperren(self, admin_passwort: str) -> bool:
"""Entsperrt das Konto bei korrektem Admin-Passwort."""
if admin_passwort == "ADMIN123":
self.gesperrt = False
self.fehlversuche = 0
return True
raise UngueltigePinError("Falsches Admin-Passwort zum Entsperren!")
def __str__(self) -> str:
"""String-Repräsentation des Kontos."""
status = "GESPERRT" if self.gesperrt else "AKTIV"
return f"Bankkonto({self.inhaber}, Stand: {self.kontostand:.2f}€, Status: {status})"
# ==============================================================================
# 3. Geldautomat mit vollständiger try-except-else-finally-Struktur
# ==============================================================================
def geldautomat_abheben(konto: Bankkonto, betrag: float, pin: str) -> str:
"""Führt eine sichere Abhebung mit try-except-else-finally durch."""
meldungen = []
try:
konto.abheben(betrag, pin)
except UngueltigePinError as e:
meldungen.append(f"PIN-Fehler: {e}")
except KontoGesperrtError as e:
meldungen.append(f"Sicherheits-Fehler: {e}")
except NichtGenugGuthabenError as e:
meldungen.append(f"Deckungs-Fehler: {e}")
except UngueltigerBetragError as e:
meldungen.append(f"Betrags-Fehler: {e}")
except BankFehler as e:
meldungen.append(f"Bank-Fehler: {e}")
else:
meldungen.append(f"Auszahlung erfolgreich: {betrag:.2f}€ ausgezahlt. Neuer Stand: {konto.kontostand:.2f}€")
finally:
meldungen.append("Bitte Karte entnehmen.")
return " | ".join(meldungen)
if __name__ == "__main__":
konto = Bankkonto("Anna", "1234", 250.0)
print(konto)
print(geldautomat_abheben(konto, 50.0, "1234"))
print(geldautomat_abheben(konto, 50.0, "9999"))
print(geldautomat_abheben(konto, 500.0, "1234"))