Protocols, Generics e Typing Avançado em Python: Técnicas para Construir Aplicações Robústas e Manuteníveis

Este artigo aborda como usar funcionalidades avançadas de tipagem em Python, como Protocols, Generics e técnicas avançadas de typing, para criar aplicações escaláveis, flexíveis e de fácil manutenção.

Protocols: Contratos Flexíveis e Estruturais

Protocols permitem definir contratos de métodos e propriedades sem herança explícita, facilitando a interoperabilidade entre microserviços. Qualquer classe que implemente os métodos definidos no protocolo pode ser usada onde esse protocolo é esperado.

Exemplo prático:

from typing import Protocol

class Serializer(Protocol):
    def serialize(self) -> bytes:
        pass

class JsonSerializer:
    def serialize(self) -> bytes:
        return b'{"user": "alice"}'

class XmlSerializer:
    def serialize(self) -> bytes:
        return b'<user>alice</user>'

def send_data(serializer: Serializer) -> None:
    data = serializer.serialize()
    print(f"Enviando dados: {data}")

send_data(JsonSerializer())
send_data(XmlSerializer())

Neste exemplo, send_data aceita qualquer objeto que implemente o método serialize, garantindo baixo acoplamento e flexibilidade.

Generics: Componentes Reutilizáveis e Tipados

Generics permitem criar classes e funções genéricas que mantêm a segurança de tipos, facilitando a modularidade.

Exemplo de repositório genérico:

from typing import TypeVar, Generic, List

T = TypeVar('T')

class Repository(Generic[T]):
    def __init__(self) -> None:
        self._items: List[T] = []

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

    def get_all(self) -> List[T]:
        return self._items

class User:
    def __init__(self, username: str) -> None:
        self.username = username

user_repo = Repository[User]()
user_repo.add(User("alice"))
for user in user_repo.get_all():
    print(user.username)

Este padrão permite criar repositórios ou caches que funcionam com qualquer tipo de objeto, aumentando a reutilização e segurança de tipos.

Tipagem Avançada: Operador | e Literal

Prefira o operador | para tipos alternativos ao invés de Union e use Literal para valores fixos, reforçando contratos claros.

Exemplo:

from typing import Literal

def login(role: Literal['admin', 'user', 'guest']) -> str:
    if role == 'admin':
        return "Acesso total"
    elif role == 'user':
        return "Acesso limitado"
    return "Acesso restrito"

print(login('admin'))  # Acesso total

Isso aumenta a legibilidade e reduz riscos de erro nas chamadas de função.

Conclusão

Combinando Protocols, Generics e tipagem avançada, é possível construir aplicações com contratos claros, flexíveis e robustos, facilitando o trabalho em times desacoplados e a manutenção do código.

Essas práticas elevam a qualidade do código e tornam os sistemas mais escaláveis e confiáveis, sendo indispensáveis para desenvolvedores focados em arquiteturas modernas, principalmente as de microserviços.



Riverfount
Vicente Eduardo Ribeiro Marçal