O sistema de tipos do Python amadureceu significativamente em 2026 com o Python 3.12+. PEP 695 (sintaxe do parâmetro de tipo), PEP 696 (padrões TypeVar) e o novotypedeclaração torna os genéricos do Python mais legíveis. Este mergulho profundo cobre padrões de tipo avançados que desenvolvedores Python experientes devem conhecer.
📋 Table of Contents
Nova sintaxe: instrução de tipo (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)
Classes e funções genéricas (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 com padrão (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)
Protocolos – Subtipagem Estrutural
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": ...
Sobrecargas – Múltiplas Assinaturas
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]
Padrões avançados TypedDict
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 — Decoradores de tipo seguro
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
O sistema de tipos do Python em 2026 com a nova sintaxe PEP 695 é significativamente mais legível do que a sintaxe baseada em TypeVar. Os principais padrões: use protocolos para digitação estrutural, ParamSpec para decoradores com segurança de tipo, TypedDict para dicionários digitados, @overload para assinaturas múltiplas e a nova instrução de tipo para aliases limpos. Execute pyright –strict ou mypy –strict para impor esses padrões.
🔗 Share this article
✍️ Leave a Comment