Zum Inhalt springen
aviral gupta

// I2.6 · ca. 40 Min. · Aufbau

Projekt: Enums und ein Bankkonto-Modell

In diesem Projekt definieren Sie Enums mit auto und StrEnum, finden Mitglieder über Wert und Name und bauen aus dem Modul ein Bankkonto-Modell mit Buchungen, einem Status und klaren Fehlern.

Lektion 6 von 6 in I2 Klassen

Ende des Moduls

Danach können Sie

  • Ein Enum oder StrEnum mit festen Werten oder auto() definieren und Name und Wert jedes Mitglieds nutzen
  • Mitglieder über Wert und Name nachschlagen, über sie iterieren und unbekannte Eingaben behandeln
  • Mitglieder mit is vergleichen und ein Kontomodell aus Enums, Datenklassen und Exceptions bauen
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen aus Lektion 2: Konstanten als Klassenvariablen. Was gibt das aus?

    class Status:
        OPEN = "open"
        CLOSED = "closed"
    
    
    status = "opne"
    print(status == Status.OPEN, status in (Status.OPEN, Status.CLOSED))
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen: Status ist jetzt ein Enum. Was gibt das aus?

    from enum import Enum
    
    
    class Status(Enum):
        OPEN = "open"
        CLOSED = "closed"
    
    
    print(Status("open"))
    try:
        Status("opne")
    except ValueError as err:
        print(err)
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie das Attribut, damit die letzte Zeile OPEN ausgibt.

    from enum import Enum
    
    
    class Status(Enum):
        OPEN = "open"
        CLOSED = "closed"
    
    
    print(Status.OPEN.____)
    Status.OPEN.)
  4. Üben · Aufgabe 4 von 7

    Die Werte kommen von auto(). Was gibt das aus?

    from enum import Enum, auto
    
    
    class Level(Enum):
        LOW = auto()
        MID = auto()
        HIGH = auto()
    
    
    print([m.value for m in Level])
  5. Üben · Aufgabe 5 von 7

    Color hat RED = 1, GREEN = 2 und BLUE = 3. Ordnen Sie jedem Ausdruck zu, was er liefert.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. Zwei Namen teilen sich den Wert 1. Was gibt das aus?

    from enum import Enum
    
    
    class Size(Enum):
        S = 1
        SMALL = 1
        M = 2
    
    
    print(len(Size), Size.SMALL)
  7. Anwenden · Aufgabe 7 von 7

    Kleine Aufgabe. Schreiben Sie ein Enum Priority mit LOW, MEDIUM und HIGH über auto(). Schreiben Sie parse_priority(text), das den Text von Leerzeichen am Rand befreit, großschreibt und das Mitglied über den Namen nachschlägt. Lösen Sie bei einem unbekannten Namen statt des KeyError ValueError("unknown priority: 'urgent'") aus. parse_priority(" high ") liefert Priority.HIGH.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Bestellstatus und Vertriebskanal

Status ist ein Enum mit Zahlen aus auto(), und ein Dict ordnet jedem Status den nächsten zu: Mitglieder sind hashbar und taugen als Schlüssel. Channel ist ein StrEnum, seine Mitglieder sind also gleich einfachen Strings und zeigen in f-Strings ihren Wert. Text von außen wie "fax" wird an einer Stelle in ein Mitglied umgewandelt, wo sich ein ValueError fangen lässt.

main.py

from enum import Enum, StrEnum, auto


class Status(Enum):
    PENDING = auto()
    PAID = auto()
    SHIPPED = auto()


class Channel(StrEnum):
    WEB = auto()
    PHONE = auto()


NEXT = {Status.PENDING: Status.PAID, Status.PAID: Status.SHIPPED}


def advance(status: Status) -> Status:
    if status not in NEXT:
        raise ValueError(f"{status.name} is the last step")
    return NEXT[status]


print([status.name for status in Status])
print(Status(2), Status["SHIPPED"].value, Status.PAID is Status(2))
print(advance(Status.PENDING), repr(advance(Status.PAID)))
print(Channel.WEB == "web", f"via {Channel.PHONE}", Channel("phone").name)
for text in ["web", "fax"]:
    try:
        print("channel:", Channel(text))
    except ValueError as err:
        print("refused:", err)
try:
    advance(Status.SHIPPED)
except ValueError as err:
    print("refused:", err)

Ausführen mit

python main.py

Ausgabe

['PENDING', 'PAID', 'SHIPPED']
Status.PAID 3 True
Status.PAID <Status.SHIPPED: 3>
True via phone PHONE
channel: web
refused: 'fax' is not a valid Channel
refused: SHIPPED is the last step
  • Status(2) schlägt über den Wert nach, Status["SHIPPED"] über den Namen; beide liefern das eine Mitgliedsobjekt.
  • repr() zeigt Name und Wert: <Status.SHIPPED: 3>.
  • print(Channel(text)) zeigt web, nicht Channel.WEB: Ein StrEnum wird wie sein Wert ausgegeben.
  • Das Dict NEXT nutzt Mitglieder als Schlüssel, und status not in NEXT erkennt den letzten Schritt.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 3

Schritt 1: Buchungsarten und Buchungen

Beginnen Sie das Bankmodell mit Beträgen in Cent. Machen Sie TxKind zu einem StrEnum mit Werten aus auto(), damit TxKind("withdrawal") funktioniert. Machen Sie Transaction zu einer eingefrorenen Datenklasse. Ihr __post_init__ löst ValueError aus, wenn cents 0 oder kleiner ist. Ergänzen Sie eine Property signed: cents bei einer Einzahlung, minus cents bei einer Abhebung.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Ersetzen Sie die String-Werte durch auto(): In einem StrEnum liefert DEPOSIT = auto() den Wert "deposit".

  2. Hinweis 2

    Schreiben Sie @dataclass(frozen=True) und lösen Sie in __post_init__(self) -> None ValueError aus, wenn self.cents <= 0.

  3. Hinweis 3

    Die Property vergleicht die Art mit is: Bei self.kind is TxKind.DEPOSIT geben Sie self.cents zurück, sonst -self.cents.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from dataclasses import dataclass
from enum import StrEnum, auto


class TxKind(StrEnum):
    DEPOSIT = auto()
    WITHDRAWAL = auto()


@dataclass(frozen=True)
class Transaction:
    kind: TxKind
    cents: int

    def __post_init__(self) -> None:
        if self.cents <= 0:
            raise ValueError(f"amount must be positive, got {self.cents}")

    @property
    def signed(self) -> int:
        if self.kind is TxKind.DEPOSIT:
            return self.cents
        return -self.cents
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from dataclasses import dataclass
from enum import StrEnum, auto


class TxKind(StrEnum):
    DEPOSIT = "DEPOSIT"
    WITHDRAWAL = "WITHDRAWAL"


@dataclass
class Transaction:
    kind: TxKind
    cents: int

test_main.py

import dataclasses

from main import Transaction, TxKind


def test_kind_values():
    """auto() liefert die kleingeschriebenen Namen als Werte"""
    got = [kind.value for kind in TxKind]
    assert got == ["deposit", "withdrawal"], f"die Werte sind {got!r}"


def test_lookup():
    """TxKind("withdrawal") findet das Mitglied"""
    assert TxKind("withdrawal") is TxKind.WITHDRAWAL, 'TxKind("withdrawal") lieferte nicht TxKind.WITHDRAWAL'


def test_signed():
    """Einzahlungen zählen positiv, Abhebungen negativ"""
    got = (Transaction(TxKind.DEPOSIT, 500).signed, Transaction(TxKind.WITHDRAWAL, 200).signed)
    assert got == (500, -200), f"signed liefert {got!r}, erwartet ist (500, -200)"


def test_rejects_amount():
    """Ein Betrag von 0 löst ValueError aus"""
    try:
        Transaction(TxKind.DEPOSIT, 0)
    except ValueError:
        pass
    else:
        assert False, "Transaction(TxKind.DEPOSIT, 0) wurde angenommen"


def test_frozen():
    """Eine Buchung lässt sich nicht ändern"""
    tx = Transaction(TxKind.DEPOSIT, 500)
    try:
        tx.cents = 1  # type: ignore[misc]
    except dataclasses.FrozenInstanceError:
        pass
    else:
        assert False, "tx.cents = 1 wurde angenommen; verwenden Sie frozen=True"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 2 von 3

Schritt 2: ein Konto mit Verlauf

Ihr Code aus Schritt 1 und zwei Exception-Klassen stehen bereit. Schreiben Sie Account um: Statt eines gespeicherten Kontostands führt es history, eine Liste von Transaction, die für jedes Konto neu ist. balance wird eine nur lesbare Property, die die Beträge mit Vorzeichen addiert. deposit und withdraw hängen eine Transaction an. withdraw löst InsufficientFundsError(balance, cents) aus, wenn der Betrag größer als der Kontostand ist.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Das Feld lautet history: list[Transaction] = field(default_factory=list). Entfernen Sie das Feld balance.

  2. Hinweis 2

    Die Property lautet @property def balance(self) -> int: return sum(tx.signed for tx in self.history).

  3. Hinweis 3

    Prüfen Sie in withdraw cents > self.balance vor dem Anhängen, damit eine abgelehnte Abhebung keine Spur hinterlässt. Transaction selbst lehnt Beträge von 0 ab.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from dataclasses import dataclass, field
from enum import StrEnum, auto


class TxKind(StrEnum):
    DEPOSIT = auto()
    WITHDRAWAL = auto()


@dataclass(frozen=True)
class Transaction:
    kind: TxKind
    cents: int

    def __post_init__(self) -> None:
        if self.cents <= 0:
            raise ValueError(f"amount must be positive, got {self.cents}")

    @property
    def signed(self) -> int:
        if self.kind is TxKind.DEPOSIT:
            return self.cents
        return -self.cents


class AccountError(Exception):
    """Something the account cannot do."""


class InsufficientFundsError(AccountError):
    def __init__(self, balance: int, requested: int) -> None:
        super().__init__(f"balance {balance} is less than {requested}")
        self.balance = balance
        self.requested = requested


@dataclass
class Account:
    owner: str
    history: list[Transaction] = field(default_factory=list)

    @property
    def balance(self) -> int:
        return sum(tx.signed for tx in self.history)

    def deposit(self, cents: int) -> None:
        self.history.append(Transaction(TxKind.DEPOSIT, cents))

    def withdraw(self, cents: int) -> None:
        if cents > self.balance:
            raise InsufficientFundsError(self.balance, cents)
        self.history.append(Transaction(TxKind.WITHDRAWAL, cents))
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from dataclasses import dataclass, field
from enum import StrEnum, auto


class TxKind(StrEnum):
    DEPOSIT = auto()
    WITHDRAWAL = auto()


@dataclass(frozen=True)
class Transaction:
    kind: TxKind
    cents: int

    def __post_init__(self) -> None:
        if self.cents <= 0:
            raise ValueError(f"amount must be positive, got {self.cents}")

    @property
    def signed(self) -> int:
        if self.kind is TxKind.DEPOSIT:
            return self.cents
        return -self.cents


class AccountError(Exception):
    """Something the account cannot do."""


class InsufficientFundsError(AccountError):
    def __init__(self, balance: int, requested: int) -> None:
        super().__init__(f"balance {balance} is less than {requested}")
        self.balance = balance
        self.requested = requested


@dataclass
class Account:
    owner: str
    balance: int = 0

    def deposit(self, cents: int) -> None:
        self.balance += cents

    def withdraw(self, cents: int) -> None:
        self.balance -= cents

test_main.py

from main import Account, InsufficientFundsError, TxKind


def test_starts_empty():
    """Ein neues Konto hat einen leeren Verlauf und Kontostand 0"""
    acct = Account("ada")
    assert (getattr(acct, "history", None), acct.balance) == ([], 0), f"history {getattr(acct, 'history', None)!r}, balance {acct.balance!r}"


def test_balance_from_history():
    """Der Kontostand wird aus dem Verlauf berechnet"""
    acct = Account("ada")
    acct.deposit(1000)
    acct.withdraw(300)
    kinds = [tx.kind for tx in getattr(acct, "history", [])]
    assert kinds == [TxKind.DEPOSIT, TxKind.WITHDRAWAL], f"der Verlauf enthält {kinds!r}"
    assert acct.balance == 700, f"balance ist {acct.balance!r}, erwartet ist 700"


def test_own_history():
    """Jedes Konto hat seinen eigenen Verlauf"""
    a = Account("ada")
    b = Account("bob")
    a.deposit(500)
    got = getattr(b, "history", None)
    assert got == [], f"nach einer Einzahlung auf a ist b.history {got!r}"


def test_insufficient_funds():
    """Zu viel abheben löst InsufficientFundsError aus"""
    acct = Account("ada")
    acct.deposit(100)
    try:
        acct.withdraw(250)
    except InsufficientFundsError as err:
        assert (err.balance, err.requested) == (100, 250), f"der Fehler enthält {(err.balance, err.requested)!r}"
    else:
        assert False, "withdraw(250) bei Kontostand 100 wurde angenommen"
    assert acct.balance == 100, f"nach der abgelehnten Abhebung ist balance {acct.balance!r}"


def test_rejects_zero():
    """deposit(0) löst ValueError aus"""
    try:
        Account("ada").deposit(0)
    except ValueError:
        pass
    else:
        assert False, "deposit(0) wurde angenommen"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Übung 3 von 3

Schritt 3: Kontostatus und importierte Zeilen

Geben Sie Status die Mitglieder OPEN und FROZEN über auto() und Account ein Feld status, das mit Status.OPEN beginnt. deposit und withdraw lösen AccountError("account is frozen") aus, wenn das Konto nicht offen ist. apply(rows) bucht Zeilen wie ("deposit", 500): Schlagen Sie die Art mit TxKind(text) nach. Eine Zeile, die mit ValueError oder AccountError scheitert, wird übersprungen und als "row 2: " plus Meldung gemeldet. Geben Sie die Meldungen zurück.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser Python herunter (bis zu 6.5 MB) und speichert es im Cache. Ihr Code bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Ergänzen Sie status: Status = Status.OPEN nach history. Ein Mitglied ist ein zulässiger Standardwert, denn es ist keine Liste.

  2. Hinweis 2

    Eine Hilfsmethode _check_open löst AccountError(f"account is {self.status.name.lower()}") aus, wenn self.status is not Status.OPEN; rufen Sie sie in deposit und withdraw zuerst auf.

  3. Hinweis 3

    Iterieren Sie mit for number, (kind_text, cents) in enumerate(rows, start=1) und fangen Sie (ValueError, AccountError) as err um Nachschlagen und Buchen herum.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

from dataclasses import dataclass, field
from enum import Enum, StrEnum, auto


class TxKind(StrEnum):
    DEPOSIT = auto()
    WITHDRAWAL = auto()


@dataclass(frozen=True)
class Transaction:
    kind: TxKind
    cents: int

    def __post_init__(self) -> None:
        if self.cents <= 0:
            raise ValueError(f"amount must be positive, got {self.cents}")

    @property
    def signed(self) -> int:
        if self.kind is TxKind.DEPOSIT:
            return self.cents
        return -self.cents


class AccountError(Exception):
    """Something the account cannot do."""


class InsufficientFundsError(AccountError):
    def __init__(self, balance: int, requested: int) -> None:
        super().__init__(f"balance {balance} is less than {requested}")
        self.balance = balance
        self.requested = requested


class Status(Enum):
    OPEN = auto()
    FROZEN = auto()


@dataclass
class Account:
    owner: str
    history: list[Transaction] = field(default_factory=list)
    status: Status = Status.OPEN

    @property
    def balance(self) -> int:
        return sum(tx.signed for tx in self.history)

    def _check_open(self) -> None:
        if self.status is not Status.OPEN:
            raise AccountError(f"account is {self.status.name.lower()}")

    def deposit(self, cents: int) -> None:
        self._check_open()
        self.history.append(Transaction(TxKind.DEPOSIT, cents))

    def withdraw(self, cents: int) -> None:
        self._check_open()
        if cents > self.balance:
            raise InsufficientFundsError(self.balance, cents)
        self.history.append(Transaction(TxKind.WITHDRAWAL, cents))

    def apply(self, rows: list[tuple[str, int]]) -> list[str]:
        problems: list[str] = []
        for number, (kind_text, cents) in enumerate(rows, start=1):
            try:
                kind = TxKind(kind_text)
                if kind is TxKind.DEPOSIT:
                    self.deposit(cents)
                else:
                    self.withdraw(cents)
            except (ValueError, AccountError) as err:
                problems.append(f"row {number}: {err}")
        return problems
Auf dem eigenen Computer ausführen

Installieren Sie Python 3.14 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.py

from dataclasses import dataclass, field
from enum import Enum, StrEnum, auto


class TxKind(StrEnum):
    DEPOSIT = auto()
    WITHDRAWAL = auto()


@dataclass(frozen=True)
class Transaction:
    kind: TxKind
    cents: int

    def __post_init__(self) -> None:
        if self.cents <= 0:
            raise ValueError(f"amount must be positive, got {self.cents}")

    @property
    def signed(self) -> int:
        if self.kind is TxKind.DEPOSIT:
            return self.cents
        return -self.cents


class AccountError(Exception):
    """Something the account cannot do."""


class InsufficientFundsError(AccountError):
    def __init__(self, balance: int, requested: int) -> None:
        super().__init__(f"balance {balance} is less than {requested}")
        self.balance = balance
        self.requested = requested



class Status(Enum):
    """Add the members OPEN and FROZEN."""


@dataclass
class Account:
    owner: str
    history: list[Transaction] = field(default_factory=list)

    @property
    def balance(self) -> int:
        return sum(tx.signed for tx in self.history)

    def deposit(self, cents: int) -> None:
        self.history.append(Transaction(TxKind.DEPOSIT, cents))

    def withdraw(self, cents: int) -> None:
        if cents > self.balance:
            raise InsufficientFundsError(self.balance, cents)
        self.history.append(Transaction(TxKind.WITHDRAWAL, cents))

    def apply(self, rows: list[tuple[str, int]]) -> list[str]:
        return []

test_main.py

from main import Account, AccountError, Status


def test_status_members():
    """Status hat OPEN und FROZEN, in dieser Reihenfolge"""
    got = [status.name for status in Status]
    assert got == ["OPEN", "FROZEN"], f"die Mitglieder sind {got!r}"


def test_starts_open():
    """Ein neues Konto ist offen"""
    got = getattr(Account("ada"), "status", None)
    assert got is Status.OPEN, f"der Status eines neuen Kontos ist {got!r}"


def test_frozen_refuses():
    """Ein eingefrorenes Konto lehnt Einzahlungen mit AccountError ab"""
    acct = Account("ada", status=Status.FROZEN)
    try:
        acct.deposit(500)
    except AccountError as err:
        assert str(err) == "account is frozen", f"die Meldung ist {str(err)!r}"
    else:
        assert False, "ein eingefrorenes Konto hat eine Einzahlung angenommen"


def test_apply_good_rows():
    """apply bucht gültige Zeilen und meldet keine Probleme"""
    acct = Account("ada")
    problems = acct.apply([("deposit", 1000), ("withdrawal", 400)])
    assert (problems, acct.balance) == ([], 600), f"Probleme {problems!r}, balance {acct.balance!r}"


def test_apply_collects_problems():
    """apply überspringt fehlerhafte Zeilen und meldet sie mit Nummer"""
    acct = Account("ada")
    rows = [("deposit", 500), ("refund", 100), ("withdrawal", 900), ("deposit", 0), ("withdrawal", 200)]
    problems = acct.apply(rows)
    expected = [
        "row 2: 'refund' is not a valid TxKind",
        "row 3: balance 500 is less than 900",
        "row 4: amount must be positive, got 0",
    ]
    assert problems == expected, f"apply lieferte {problems!r}"
    assert acct.balance == 300, f"balance ist {acct.balance!r}, erwartet ist 300"

Unter macOS und Linux tippen Sie python3, wo in diesen Befehlen python steht, wie in der ersten Lektion.

Programm ausführen:

python main.py

Prüfungen ausführen (learnrun.py muss im selben Ordner liegen):

python learnrun.py test
learnrun.py herunterladen

Häufige Fehler

Einen Wert nachschlagen, den kein Mitglied hat

from enum import Enum


class Status(Enum):
    OPEN = "open"
    CLOSED = "closed"


status = Status("opne")

Was Python ausgibt

ValueError: 'opne' is not a valid Status

Warum, und die Lösung

Dieser Fehler ist genau die Aufgabe des Enums: Der Text ist keiner der erlaubten Werte. Kommt der Text von Nutzern oder aus einer Datei, wandeln Sie ihn an einer Stelle um und fangen dort ValueError, um erneut zu fragen oder die Zeile zu melden. Greifen Sie nicht auf den rohen String zurück, sonst wandert der Tippfehler weiter.

Enum-Mitglieder mit < ordnen

from enum import Enum


class Level(Enum):
    LOW = 1
    HIGH = 2


print(Level.LOW < Level.HIGH)

Was Python ausgibt

TypeError: '<' not supported between instances of 'Level' and 'Level'

Warum, und die Lösung

Enum-Mitglieder unterstützen == und is, aber nicht <, auch wenn ihre Werte Zahlen sind. Vergleichen Sie stattdessen die Werte, Level.LOW.value < Level.HIGH.value, oder halten Sie eine ausdrückliche Reihenfolge fest, etwa list(Level).index(member).

Einen Namen in falscher Schreibweise nachschlagen

from enum import Enum


class Color(Enum):
    RED = 1
    GREEN = 2


print(Color["red"])

Was Python ausgibt

KeyError: 'red'

Warum, und die Lösung

Eckige Klammern schlagen den Mitgliedsnamen genau so nach, wie er in der Klasse steht, hier RED. Normalisieren Sie den Text zuerst, Color[text.strip().upper()], und fangen Sie KeyError für Namen, die es trotzdem nicht gibt. Ist der Text ein Wert und kein Name, nehmen Sie runde Klammern.

Python im Browser: Pyodide 314.0.7, MPL-2.0. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

Ein Enum ist eine feste Menge benannter Werte

class Status(Enum) mit OPEN = "open" und CLOSED = "closed" erzeugt genau zwei Mitglieder. Jedes hat einen Namen, Status.OPEN.name ist "OPEN", und einen Wert, Status.OPEN.value ist "open". Mitglieder lassen sich nicht neu zuweisen, und ein Tippfehler wie Status.OPNE scheitert sofort, statt still einen falschen String zu speichern. Wenn die Werte egal sind, schreiben Sie OPEN = auto(): Enum nummeriert die Mitglieder 1, 2, 3, StrEnum nimmt den kleingeschriebenen Namen. print(Status.OPEN) zeigt Status.OPEN.

Nachschlagen und Iterieren

Status("open") findet das Mitglied mit diesem Wert, Status["OPEN"] das Mitglied mit diesem Namen. Ein unbekannter Wert löst ValueError aus, ein unbekannter Name KeyError. Wandeln Sie Eingaben von außen deshalb einmal am Rand des Programms in ein Mitglied um und fangen Sie den Fehler dort ab. for status in Status liefert die Mitglieder in der Reihenfolge ihrer Definition, len(Status) zählt sie. Ein zweiter Name für einen vorhandenen Wert ist ein Alias: Er findet dasselbe Mitglied, beim Iterieren wird er übersprungen.

Vergleichen, und das Projekt

Jedes Mitglied existiert genau einmal, also vergleichen Sie mit is: status is Status.OPEN. == funktioniert auch, aber ein Mitglied ist nie gleich seinem rohen Wert, und < wird nicht unterstützt. StrEnum-Mitglieder sind zugleich Strings: Kind.DEPOSIT == "deposit" ist True, und ein f-String zeigt den Wert. Mitglieder sind hashbar, taugen also als Dict-Schlüssel und Standardwerte in Datenklassen. Das Projekt verbindet das Modul: ein StrEnum für Buchungsarten, eine eingefrorene Transaction, ein Account mit eigenem Verlauf, berechnetem Kontostand, eigenen Exceptions und einem Status-Enum.

Quellen

Zuletzt geprüft am 29. September 2026