A PEP 483 estabelece a base teórica para o sistema de anotações de tipo no Python, introdzuido na versão 3.5. Este documento explora os conceitos fundamentais que permitem a verificação estática de tipos em uma linguagem intrinsicamente dinâmica.
Tipos e Relações de Subtipagem
Um tipo é definido como um conjunto de valores e as operações aplicáveis a esses valores. A subtipagem ocorre quando um tipo T1 é considerado um subtipo de T2 se todos os valores de T1 também forem válidos em T2, e T1 suportar todas as operações de T2 (podendo ter adicionais).
Existem duas abordagens principais para declarar subtipagem:
- Subtiapgem Nominal: Baseada na hierarquia de herança de classes.
- Subtipagem Estrutural: Baseada na presença de métodos e atributos específicos, independentemente da herança.
class Veiculo:
def mover(self) -> None: ...
class Carro(Veiculo):
def mover(self) -> None: ...
def buzinhar(self) -> None: ...
# Carro é um subtipo de Veiculo (Nominal)
Tipagem Gradual e Consistência
A tipagem gradual permite anotar apenas partes do código, mesclando tipagem estática e dinâmica. A compatibilidade é regida pela relação de "consistência", que difere da subtipagem estrita ao introduzir o tipo Any:
- Se
T1é subtipo deT2,T1é consistente comT2. Anyé consistente com qualquer tipo.- Qualquer tipo é consistente com
Any.
class Forma: ...
class Circulo(Forma): ...
elemento: Forma = Circulo() # Válido (Subtipagem)
dinamico: Any = obter_dados()
elemento = dinamico # Válido (Consistência com Any)
Construtores de Tipos Fundamentais
O módulo typing fornece blocos de construção para tipos complexos:
- Union: Aceita múltiplos tipos.
Union[int, str]. - Optional: Atalho para
Union[T, None]. - Tuple: Define tuplas de tamanho fixo ou variável.
Tuple[int, float]ouTuple[str, ...]. - Callable: Tipa funções e callables.
Callable[[int, str], bool].
Tipos Genéricos e Variáveis de Tipo
Genéricos permitem parametrizar classes e funções com tipos. Utiliza-se TypeVar para criar variáveis de tipo.
from typing import TypeVar, Generic, Sequence
Item = TypeVar('Item')
class Pilha(Generic[Item]):
def __init__(self) -> None:
self._dados: list[Item] = []
def empilhar(self, valor: Item) -> None:
self._dados.append(valor)
def desempilhar(self) -> Item:
return self._dados.pop()
def obter_ultimo(seq: Sequence[Item]) -> Item:
return seq[-1]
Variância: Covariância, Contravariância e Invariância
A variância define como a subtipagem de tipos copmlexos se relaciona com a subtipagem de seus parâmetros:
- Covariante: A direção da subtipagem é preservada. Comum em estruturas imutáveis ou de somente leitura.
- Contravariante: A direção da subtipagem é invertida. Típico de parâmetros de entrada em callables.
- Invariante: Não há relação de subtipagem. Padrão para estruturas mutáveis.
T_co = TypeVar('T_co', covariant=True)
T_contra = TypeVar('T_contra', contravariant=True)
class CacheLeitura(Generic[T_co]): # Covariante
def obter(self) -> T_co: ...
class ProcessadorDados(Generic[T_contra]): # Contravariante
def consumir(self, dado: T_contra) -> None: ...
class BufferMutavel(Generic[Item]): # Invariante
def ler(self) -> Item: ...
def escrever(self, valor: Item) -> None: ...
Recursos Práticos
Para facilitar o uso diário, o sistema de tipos suporta:
- Aliases de Tipo:
Coordenada = Tuple[float, float]. - Referências Diretivas: Uso de strings para classes ainda não definidas, como
def comparar(self, outro: 'No') -> int. - Conversão Explícita: Uso de
cast(Tipo, objeto)para forçar uma tipagem específica sem verificação em tempo de execução.