Unpack no Python: kwargs e tipos variádicos

Publicado em: 29/08/2026
Tempo de leitura: 6 minutos
Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.

typing.Unpack representa a expansão estática de uma estrutura de tipos. Ele aparece em dois cenários principais: tipar argumentos nomeados recebidos por **kwargs a partir de um TypedDict e expandir uma tupla variádica baseada em TypeVarTuple. Em ambos os casos, Unpack permite que o analisador enxergue elementos individuais que, sem a anotação, ficariam escondidos dentro de uma coleção genérica.

Neste guia, você aprenderá a usar Unpack em funções, wrappers, factories, callbacks, classes genéricas e arrays tipados; entenderá Required, NotRequired, TypeVarTuple, compatibilidade de assinaturas, encaminhamento de kwargs e limites de runtime.

O problema de **kwargs genérico

def conectar(**opcoes: object) -> None:
    ...

conectar(host="localhost", porta=5432, ssl=True)

A assinatura aceita qualquer nome e qualquer valor. O editor não sugere parâmetros, o analisador não detecta erros de digitação e chamadas inválidas parecem corretas.

TypedDict com Unpack

from typing import TypedDict, Unpack

class OpcoesConexao(TypedDict):
    host: str
    porta: int


def conectar(**opcoes: Unpack[OpcoesConexao]) -> None:
    host = opcoes["host"]
    porta = opcoes["porta"]

Agora host e porta são argumentos nomeados obrigatórios. O analisador conhece seus tipos e pode rejeitar nomes extras ou valores incompatíveis.

Campos opcionais

from typing import NotRequired

class OpcoesConexao(TypedDict):
    host: str
    porta: int
    timeout: NotRequired[float]
    ssl: NotRequired[bool]

Required e NotRequired definem quais kwargs são obrigatórios. O artigo sobre Required e NotRequired no Python detalha presença e nulabilidade.

Chamada correta e incorreta

conectar(host="db.local", porta=5432)
conectar(host="db.local", porta=5432, timeout=3.0)

conectar(host="db.local")              # falta porta
conectar(host="db.local", porta="5432") # tipo errado
conectar(host="db.local", porta=5432, retries=2) # nome extra

A validação ocorre durante análise estática. Em runtime, **kwargs continua sendo um dicionário normal.

Dentro da função

O parâmetro opcoes é tratado como o TypedDict correspondente. Chaves obrigatórias podem ser acessadas diretamente; chaves opcionais exigem teste ou valor padrão.

def conectar(**opcoes: Unpack[OpcoesConexao]) -> None:
    timeout = opcoes.get("timeout", 5.0)
    usar_ssl = opcoes.get("ssl", False)

Encaminhando kwargs

def conectar_com_log(**opcoes: Unpack[OpcoesConexao]) -> None:
    print("conectando")
    conectar(**opcoes)

O wrapper preserva os nomes e tipos. Se ele aceitasse apenas **opcoes: object, a relação seria perdida e o encaminhamento exigiria cast ou seria menos seguro.

Adicionar parâmetros explícitos

def conectar_com_log(
    nivel: str,
    **opcoes: Unpack[OpcoesConexao],
) -> None:
    ...

Parâmetros anteriores continuam normais. Cuidado para não criar um nome explícito que também exista no TypedDict, pois a assinatura teria conflito.

Conflitos de nomes

class Opcoes(TypedDict):
    nivel: str

# Evite combinar nivel explícito e Unpack[Opcoes]

Cada argumento nomeado deve aparecer uma única vez. Ferramentas de tipos devem sinalizar sobreposição entre parâmetros declarados e chaves expandidas.

kwargs extras

Unpack de TypedDict normalmente descreve um conjunto fechado de nomes. Se a função realmente aceita parâmetros arbitrários adicionais, talvez TypedDict não represente a API. Considere um mapping explícito, sobrecargas, um objeto de configuração ou separar opções conhecidas de metadados livres.

Modelando callbacks

from collections.abc import Callable

class EventoKwargs(TypedDict):
    usuario_id: int
    acao: str

Callback = Callable[..., None]

Expressar kwargs específicos em Callable pode exigir Protocol com método __call__:

from typing import Protocol

class CallbackEvento(Protocol):
    def __call__(self, **dados: Unpack[EventoKwargs]) -> None: ...

Assim, implementações precisam aceitar os nomes esperados.

Unpack em métodos

class Cliente:
    def requisitar(self, **opcoes: Unpack[OpcoesConexao]) -> None:
        ...

O comportamento é igual ao de funções. self não faz parte do TypedDict expandido.

Factories de objetos

class Configuracao(TypedDict, total=False):
    cache: bool
    timeout: float

class Servico:
    def __init__(self, nome: str, **config: Unpack[Configuracao]) -> None:
        ...

A factory oferece autocompletar para opções sem criar uma lista longa de parâmetros. Porém, parâmetros explícitos costumam ser melhores quando a API é pequena e estável.

Unpack e herança de TypedDict

class BaseHttp(TypedDict, total=False):
    timeout: float
    headers: dict[str, str]

class OpcoesGet(BaseHttp, total=False):
    params: dict[str, str]

Unpack[OpcoesGet] inclui as chaves herdadas. Hierarquias profundas dificultam descobrir a assinatura real; mantenha os tipos simples.

ReadOnly em kwargs

ReadOnly descreve escrita dentro de TypedDict, mas kwargs são construídos para cada chamada. Marcar uma opção como ReadOnly raramente traz o mesmo benefício que em registros persistentes. Use qualificadores apenas quando a semântica for clara e suportada pelo analisador.

Unpack com TypeVarTuple

O segundo grande uso é expandir uma sequência de parâmetros de tipo:

from typing import Generic, TypeVarTuple, Unpack

Dimensoes = TypeVarTuple("Dimensoes")

class Array(Generic[Unpack[Dimensoes]]):
    ...

Uma especialização pode ter quantidade variável de dimensões:

imagem: Array[int, int, int]
matriz: Array[int, int]

Os elementos após Array representam uma tupla de tipos expandida, não uma única tupla.

Sintaxe com estrela

Versões modernas permitem formas equivalentes com * em alguns contextos:

class Array[*Dimensoes]:
    ...

Unpack[Dimensoes] continua importante para compatibilidade e para contextos onde a sintaxe estrelada não é aceita.

Tuplas variádicas

Ts = TypeVarTuple("Ts")

def adicionar_prefixo(
    valores: tuple[Unpack[Ts]],
) -> tuple[str, Unpack[Ts]]:
    return ("prefixo", *valores)

A função preserva todos os tipos da tupla original e adiciona uma string no início.

Preservando formas

Bibliotecas numéricas podem representar eixos ou dimensões no sistema de tipos. Uma operação pode manter a forma, adicionar eixo ou remover dimensão:

Shape = TypeVarTuple("Shape")

class Tensor(Generic[Unpack[Shape]]):
    ...

def batch(x: Tensor[Unpack[Shape]]) -> Tensor[int, Unpack[Shape]]:
    ...

Esse modelo é avançado e depende do suporte do analisador. Ele não valida tamanhos numéricos em runtime.

Apenas um TypeVarTuple por lista

Uma lista de parâmetros não pode conter múltiplos grupos variádicos ambíguos. O analisador precisa saber como dividir os argumentos entre eles. Estruture a API com prefixos e sufixos fixos em torno de um único grupo.

TypeVarTuple não é tuple[T, …]

tuple[T, ...] representa quantidade variável de elementos do mesmo tipo. TypeVarTuple representa quantidade variável de tipos possivelmente diferentes:

tuple[int, str, bytes]
# Ts pode representar (int, str, bytes)

Unpack não executa transformação

Em runtime, a anotação não desempacota dados nem valida argumentos. O operador ** da chamada e o operador * da tupla continuam responsáveis pelo comportamento. Unpack descreve a expansão para ferramentas estáticas.

Compatibilidade de versões

Use typing_extensions.Unpack e TypeVarTuple quando necessário em versões anteriores. Verifique também a versão do mypy, pyright ou outro analisador, pois o suporte a recursos variádicos evolui.

Erros comuns

  • Anotar **kwargs como TypedDict sem Unpack: isso significa que cada valor seria um TypedDict, não que as chaves foram expandidas.
  • Esperar validação em runtime: kwargs continuam sendo dict.
  • Permitir nomes extras sem modelá-los: Unpack descreve o conjunto conhecido.
  • Criar conflito com parâmetro explícito: uma chave não pode aparecer duas vezes.
  • Confundir TypeVarTuple com tupla homogênea: ele preserva tipos posicionais distintos.
  • Usar variádicos em APIs simples: parâmetros explícitos podem ser mais claros.

Exemplo completo: cliente HTTP

from typing import NotRequired, TypedDict, Unpack

class OpcoesHttp(TypedDict, total=False):
    timeout: float
    headers: dict[str, str]
    seguir_redirects: bool
    tentativas: int


def get(
    url: str,
    **opcoes: Unpack[OpcoesHttp],
) -> bytes:
    timeout = opcoes.get("timeout", 10.0)
    headers = opcoes.get("headers", {})
    seguir = opcoes.get("seguir_redirects", True)
    tentativas = opcoes.get("tentativas", 1)
    return realizar_get(url, timeout, headers, seguir, tentativas)

O editor sugere quatro opções nomeadas, rejeita erros de digitação e preserva tipos. A implementação recebe um dicionário normal e aplica padrões.

Quando evitar Unpack

Prefira parâmetros explícitos quando há poucas opções estáveis, pois documentação e introspecção ficam mais simples. Use uma dataclass de configuração quando opções são compartilhadas por várias chamadas ou precisam de validação. Use Unpack quando a API já é naturalmente baseada em kwargs e você quer descrevê-la com precisão.

Conclusão

typing.Unpack torna expansões visíveis ao sistema de tipos. Com TypedDict, ele cria kwargs com nomes, tipos e obrigatoriedade conhecidos. Com TypeVarTuple, permite genéricos com quantidade variável de parâmetros posicionais.

A documentação oficial de Unpack no módulo typing detalha as formas suportadas. Use-o para preservar assinaturas e relações variádicas, mas mantenha validação de runtime e escolha uma API mais simples quando parâmetros explícitos ou objetos de configuração forem suficientes.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Required e NotRequired: campos opcionais no TypedDict

    Aprenda Required e NotRequired no Python para controlar chaves obrigatórias e opcionais em TypedDict com contratos claros.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Detailed view of programming code in a dark theme on a computer screen.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ReadOnly no Python: proteja campos TypedDict

    Aprenda ReadOnly no Python para marcar campos TypedDict como somente leitura, modelar contratos imutáveis e evitar alterações acidentais.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    A detailed image of a reticulated python showcasing its patterned scales and intricate skin texture.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TypeAliasType no Python: aliases em runtime

    Aprenda TypeAliasType no Python para criar aliases explícitos, inspecionar tipos em runtime e modelar APIs genéricas com segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    29/08/2026
    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    overload no Python: assinaturas precisas

    Aprenda typing.overload no Python para criar assinaturas precisas com Literal, None, genéricos, métodos e retornos dependentes dos argumentos.

    Ler mais

    Tempo de leitura: 8 minutos
    29/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ClassVar no Python: separe classe e instância

    Aprenda ClassVar no Python para separar atributos de classe e instância em dataclasses, registries, caches, herança e contadores.

    Ler mais

    Tempo de leitura: 7 minutos
    29/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Final no Python: proteja constantes e herança

    Aprenda Final e @final no Python para proteger constantes, atributos, métodos e classes, entendendo os limites em runtime.

    Ler mais

    Tempo de leitura: 7 minutos
    29/08/2026