Verstehe, was Annotationen zur Laufzeit wirklich tun, und hol das Beste aus Dataclasses heraus - mit frozen, slots und default_factory.
Diese Lektion in Kodokon öffnenTyp-Annotationen sind Metadaten, keine Einschränkungen: Der Interpreter speichert sie in __annotations__ und prüft sie zur Laufzeit nie. Das Prüfen ist die Aufgabe eines statischen Analysewerkzeugs wie mypy oder pyright, vor der Ausführung. Die direkte Folge: Ein schlecht typisiertes Programm läuft ohne Murren - und das ist so gewollt. Schrittweise Typisierung erlaubt dir, eine bestehende Codebasis nach und nach zu annotieren, ohne irgendetwas zu zerbrechen.
from typing import get_type_hints
def add(a: int, b: int) -> int:
return a + b
print(add("py", "thon"))
print(add.__annotations__)
print(get_type_hints(add))Seit Python 3.9 sind eingebaute Typen direkt generisch: Schreib list[int] oder dict[str, float], ohne typing.List zu importieren. Unions schreibt man seit 3.10 als int | None. Um den Eingabetyp an den Ausgabetyp zu koppeln, nutze einen TypeVar - Python 3.12 bietet sogar die kompakte Syntax def first[T](items: list[T]) -> T. Ein oft übersehener Punkt: from __future__ import annotations macht jede Annotation lazy (als Strings gespeichert), was Verweise auf weiter unten definierte Klassen erlaubt und die Kosten beim Import senkt.
from collections.abc import Iterable, Iterator
from typing import TypeVar
T = TypeVar("T")
def dedupe(items: Iterable[T]) -> Iterator[T]:
seen: set[T] = set()
for item in items:
if item not in seen:
seen.add(item)
yield item
print(list(dedupe([3, 1, 3, 2, 1])))from dataclasses import dataclass, field
@dataclass(frozen=True, slots=True)
class Point:
x: float
y: float
@dataclass
class Basket:
items: list[str] = field(default_factory=list)
total: int = 0
def __post_init__(self) -> None:
self.total = len(self.items)
point = Point(1.0, 2.0)
print(point, hash(point))
print(Basket(["book", "pen"]))Drei Parameter machen aus @dataclass ein Werkzeug für den Produktivbetrieb. frozen=True macht Instanzen unveränderlich und, zusammen mit der generierten Gleichheit, hashbar - nutzbar als Dictionary-Schlüssel. slots=True (Python 3.10+) entfernt das __dict__ pro Instanz: weniger Speicher, schnellerer Attributzugriff, und jede unerwartete Attributzuweisung schlägt fehl. Schließlich erzwingt kw_only=True explizite Aufrufe mit Schlüsselwörtern. In __post_init__ gehören Invarianten und abgeleitete Felder. Für ein einfaches unveränderliches Aggregat ist NamedTuple weiterhin sinnvoll; sobald es Verhalten oder Validierung gibt, bevorzuge die Dataclass.
add('py', 'thon') aufrufst, während die Funktion (a: int, b: int) -> int annotiert ist?items: list[str] = [] als Standardwert eines Dataclass-Feldes verboten?list[str] keine gültige Annotation für ein Feld istfrozen=True mit einer Dataclass?