Protocolos e Tipagem Estrutural
typing.Protocol habilita a tipagem dinâmica estática: qualquer classe com métodos correspondentes satisfaz o protocolo sem precisar herdar dele. Este é o companheiro da camada de tipagem para padrões estruturais de OOP.
Receita
from typing import Protocol
class SupportsWrite(Protocol):
def write(self, data: bytes) -> int: ...
def persist(stream: SupportsWrite, payload: bytes) -> None:
stream.write(payload)Quando usar:
- Para tipar objetos semelhantes a arquivos e streams.
- Para definir portas na arquitetura hexagonal.
- Para testes com fakes sem herança de ABC.
- Para tipagem gradual em código legado com tipagem dinâmica.
Exemplo Funcional
from typing import Protocol, runtime_checkable
@runtime_checkable
class Repository(Protocol):
def get(self, item_id: int) -> dict[str, object]: ...
def save(self, item: dict[str, object]) -> None: ...
class InMemoryRepo:
def __init__(self) -> None:
self._data: dict[int, dict[str, object]] = {}
def get(self, item_id: int) -> dict[str, object]:
return self._data[item_id]
def save(self, item: dict[str, object]) -> None:
self._data[int(item["id"])] = item
def service(repo: Repository) -> None:
repo.save({"id": 1, "name": "Ada"})
assert repo.get(1)["name"] == "Ada"
if __name__ == "__main__":
service(InMemoryRepo())
print(isinstance(InMemoryRepo(), Repository))O que isto demonstra:
InMemoryReposatisfazRepositorysem herança.- O serviço depende da interface estrutural.
@runtime_checkablehabilitaisinstanceem testes.- Métodos usam corpos de elipse
...na definição do Protocolo.
Mergulho Profundo
Como Funciona
- PEP 544 - Subtipagem estrutural para verificadores estáticos.
- Nominal vs. Estrutural - ABC requer herança; Protocolo não.
- runtime_checkable -
isinstanceverifica apenas nomes de métodos - superficial. - Herança de Protocolo - Estenda protocolos para adicionar métodos.
@propertyem Protocolo - Declare atributos somente leitura estruturalmente.
Protocolo vs. ABC
| Necessidade | Escolha |
|---|---|
| Correspondência estrutural de terceiros | Protocolo |
| Forçar na instanciação | ABC |
Validação profunda com isinstance | Nenhum dos dois sozinho - testes + tipos |
Notas de Python
class Readable(Protocol):
def read(self, n: int = -1) -> bytes: ...
# Protocolo genérico
from typing import TypeVar
T = TypeVar("T", contravariant=True)
class Box(Protocol[T]):
def put(self, item: T) -> None: ...Armadilhas
- Falsa confiança com
isinstance-runtime_checkableé superficial. Correção: Não confie para limites de segurança. - Herança de tempo de execução de Protocolo - Apenas para tipagem - não espere comportamento de base de Protocolo em tempo de execução.
- Métodos privados - Protocolo não pode ver métodos com name mangling. Correção: Apenas métodos de interface pública.
- Derivação estrutural - Mudanças na implementação quebram o verificador silenciosamente nos locais de chamada. Correção: Use CI com mypy em ambos os lados.
- Definições duplicadas de Protocolo - Equivalência estrutural não é automática entre módulos. Correção: Importe o tipo de Protocolo compartilhado.
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| ABC | Controlar a árvore de herança | Tipos de terceiros |
object | Sem ajuda estática | Precisa de contratos de método |
| zope.interface | Ecossistema legado | Código novo focado em tipagem |
| classe concreta única | Uma implementação | Múltiplas formas |
FAQs
herdar Protocolo?
Opcional para clareza de tipagem; não é necessário para correspondência estrutural em tempo de execução.
Protocolo com propriedades?
Declare @property def x(self) -> int: ... no corpo do protocolo.
suporte a protocolo pyright?
Forte - muitas vezes mais rigoroso que mypy em casos extremos de sobreposição de protocolos.
Protocolo para chamáveis?
Use Protocol com __call__ ou Callable - Callable é mais simples apenas para funções.
total_match?
Não embutido - garanta que todos os métodos do protocolo sejam implementados; verificadores relatam os ausentes.
protocolo de array numpy?
Bibliotecas definem Protocolos que correspondem à API dinâmica do ndarray sem importar numpy em stubs somente de tipos.
FastAPI Depends typing?
Dependa de serviços tipados com Protocolo para test doubles em funções de rota.
protocolo congelado?
Protocolos em si não impõem imutabilidade - documente as expectativas de implementação.
typing_extensions?
3.14 tem Protocol na biblioteca padrão; versões mais antigas usavam o backport do typing_extensions.
sobreposição com artigo de OOP?
O artigo de OOP foca em padrões de tempo de execução; esta página foca na configuração do verificador estático e no design de API.
Relacionados
- Protocolos e Tipagem Estrutural - visão de OOP em tempo de execução
- Classes Base Abstratas - alternativa nominal
- Genéricos e TypeVar - protocolos genéricos
- Configuração de mypy & pyright - configuração do verificador
Versões da Pilha: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.