🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

Deep Dive zum Python-Typsystem 2026: PEP 695, Protokolle und Generics

⏱️5 min read  ·  887 words

Das Typsystem von Python ist im Jahr 2026 mit Python 3.12+ deutlich ausgereifter geworden. PEP 695 (Typparametersyntax), PEP 696 (TypeVar-Standardeinstellungen) und das neuetype-Anweisung machen Python-Generika lesbarer. Dieser ausführliche Einblick behandelt fortgeschrittene Typmuster, die erfahrene Python-Entwickler kennen sollten.

Neue Syntax: type-Anweisung (Python 3.12+)

# Old way (still works)
from typing import TypeAlias, TypeVar
Vector = list[float]
T = TypeVar("T")

# New syntax (Python 3.12+, much cleaner)
type Vector = list[float]
type Matrix = list[Vector]
type JSON = str | int | float | bool | None | list["JSON"] | dict[str, "JSON"]

# Generic type alias
type Stack[T] = list[T]
type Pair[T, U] = tuple[T, U]
type Callback[T] = callable[[T], None]

# Use them
def push[T](stack: Stack[T], item: T) -> Stack[T]:
    return [*stack, item]

numbers: Stack[int] = [1, 2, 3]
result = push(numbers, 4)

Generische Klassen und Funktionen (Python 3.12+)

# Old TypeVar syntax (still valid)
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T | None:
    return items[0] if items else None

# New syntax (PEP 695)
def first[T](items: list[T]) -> T | None:
    return items[0] if items else None

def map_list[T, U](items: list[T], fn: callable[[T], U]) -> list[U]:
    return [fn(item) for item in items]

class Stack[T]:
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        if not self._items:
            raise IndexError("Stack is empty")
        return self._items.pop()

    def peek(self) -> T | None:
        return self._items[-1] if self._items else None

    def __len__(self) -> int:
        return len(self._items)

# Usage
stack: Stack[str] = Stack()
stack.push("hello")
stack.push("world")
print(stack.pop())  # "world"

# Constrained TypeVar
def sort_items[T: (int, str, float)](items: list[T]) -> list[T]:
    return sorted(items)

TypeVar mit Standard (PEP 696 – Python 3.13+)

from typing import TypeVar

# TypeVar with default
T = TypeVar("T", default=str)

class Container[T = str]:
    def __init__(self, value: T) -> None:
        self.value = value

    def get(self) -> T:
        return self.value

# T defaults to str if not specified
c1: Container = Container("hello")  # T = str (default)
c2: Container[int] = Container(42)  # T = int (explicit)

Protokolle – Strukturelle Subtypisierung

from typing import Protocol, runtime_checkable
from collections.abc import Sequence

@runtime_checkable
class Sizeable(Protocol):
    def __len__(self) -> int: ...

@runtime_checkable
class Comparable[T](Protocol):
    def __lt__(self, other: T) -> bool: ...
    def __le__(self, other: T) -> bool: ...
    def __gt__(self, other: T) -> bool: ...
    def __ge__(self, other: T) -> bool: ...

# Any class with __len__ satisfies Sizeable — no inheritance needed
def count_items(collection: Sizeable) -> int:
    return len(collection)

print(count_items([1, 2, 3]))        # works: list has __len__
print(count_items({"a": 1}))        # works: dict has __len__
print(count_items("hello"))          # works: str has __len__

# isinstance check (requires @runtime_checkable)
print(isinstance([1, 2], Sizeable))  # True

# Protocol with methods
class Serializable(Protocol):
    def to_json(self) -> str: ...
    def to_dict(self) -> dict: ...

    @classmethod
    def from_json(cls, json_str: str) -> "Serializable": ...

Überladungen – Mehrere Signaturen

from typing import overload

@overload
def process(x: int) -> int: ...
@overload
def process(x: str) -> str: ...
@overload
def process(x: list[int]) -> list[int]: ...

def process(x):
    if isinstance(x, int):
        return x * 2
    elif isinstance(x, str):
        return x.upper()
    elif isinstance(x, list):
        return [item * 2 for item in x]

# Type checker knows the return type based on input type
result_int: int = process(5)      # type checker: int
result_str: str = process("hi")   # type checker: str
result_list: list[int] = process([1, 2, 3])  # type checker: list[int]

TypedDict Erweiterte Muster

from typing import TypedDict, Required, NotRequired

# Partial TypedDict — some required, some optional
class UserCreate(TypedDict):
    name: Required[str]
    email: Required[str]
    password: Required[str]
    role: NotRequired[str]   # optional

# Inheritance
class UserUpdate(TypedDict, total=False):
    name: str
    email: str
    role: str

# Nested TypedDict
class Address(TypedDict):
    street: str
    city: str
    country: str
    zip_code: NotRequired[str]

class UserWithAddress(TypedDict):
    id: int
    name: str
    address: Address

# Use in function signatures
def create_user(user: UserCreate) -> int:
    return db.insert("users", user)

def update_user(user_id: int, updates: UserUpdate) -> bool:
    return db.update("users", user_id, updates)

ParamSpec – Typsichere Dekoratoren

from typing import ParamSpec, TypeVar, Callable
import functools

P = ParamSpec("P")
R = TypeVar("R")

def retry(max_attempts: int = 3):
    def decorator(func: Callable[P, R]) -> Callable[P, R]:
        @functools.wraps(func)
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
            raise RuntimeError("unreachable")
        return wrapper
    return decorator

@retry(max_attempts=3)
def fetch_data(url: str, timeout: int = 30) -> dict:
    return requests.get(url, timeout=timeout).json()

# Type checker knows: fetch_data(url: str, timeout: int = 30) -> dict
# The decorator is fully transparent to the type system

Das Typsystem von Python im Jahr 2026 mit der neuen PEP 695-Syntax ist deutlich lesbarer als die TypeVar-basierte Syntax. Die Schlüsselmuster: Verwenden Sie Protocols für strukturelle Typisierung, ParamSpec für typsichere Dekoratoren, TypedDict für typisierte Wörterbücher, @overload für mehrere Signaturen und die neue Typanweisung für saubere Aliase. Führen Sie pyright –strict oder mypy –strict aus, um diese Muster durchzusetzen.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা