Literal no Python: restrinja valores

Publicado em: 28/08/2026
Tempo de leitura: 5 minutos
A close-up of a padlock securing a wire fence, symbolizing protection and safety.

Uma anotação como str informa que uma função aceita texto, mas não informa quais textos são válidos. Muitos parâmetros admitem apenas um conjunto fechado de valores, como "json", "csv" ou "xml". typing.Literal permite expressar esses valores exatos na assinatura, melhorando autocomplete, documentação e análise estática.

Neste guia, você aprenderá a usar Literal com strings, números, booleanos e enums, criar overloads, discriminar variantes de TypedDict, validar match/case, definir aliases reutilizáveis e entender por que Literal não substitui a validação em runtime.

O que Literal representa?

Literal descreve um valor específico, não apenas seu tipo geral.

from typing import Literal

Formato = Literal["json", "csv", "xml"]

def exportar(dados: list[dict], formato: Formato) -> bytes:
    ...

exportar([], "json")
# exportar([], "yaml")  # erro no analisador estático

Em runtime, Formato não bloqueia o valor. A função ainda precisa validar entradas vindas de usuários, arquivos ou rede.

Quando Literal melhora uma API?

Literal é útil quando o conjunto de opções é pequeno, estável e faz parte do contrato público. Ele elimina strings mágicas espalhadas e torna chamadas incorretas visíveis antes da execução.

ModoAbertura = Literal["leitura", "escrita", "anexar"]


def abrir_recurso(caminho: str, modo: ModoAbertura) -> None:
    ...

O editor consegue sugerir os valores permitidos. Isso é mais informativo do que um parâmetro str acompanhado apenas de comentário.

Strings, números e booleanos

Literal aceita valores literais compatíveis com o sistema de typing.

Nivel = Literal[0, 1, 2, 3]
Direcao = Literal["norte", "sul", "leste", "oeste"]
Ativo = Literal[True]


def configurar(nivel: Nivel, direcao: Direcao, ativo: Ativo) -> None:
    ...

Literal[True] é mais específico que bool. Ele pode ser útil em overloads, embora APIs com parâmetros booleanos frequentemente fiquem mais claras com funções separadas ou enums.

Aliases reutilizáveis

Quando uma lista de valores aparece em várias funções, crie um alias.

from typing import Literal, TypeAlias

LogLevel: TypeAlias = Literal["debug", "info", "warning", "error"]


def log(mensagem: str, nivel: LogLevel = "info") -> None:
    ...

Aliases evitam duplicação e facilitam evolução. Alterar o conjunto em um local atualiza todas as assinaturas.

Literal com overload

Um dos usos mais poderosos é relacionar um valor exato ao tipo retornado.

from typing import Literal, overload

@overload
def carregar(caminho: str, *, binario: Literal[True]) -> bytes:
    ...

@overload
def carregar(caminho: str, *, binario: Literal[False] = False) -> str:
    ...

def carregar(caminho: str, *, binario: bool = False) -> str | bytes:
    modo = "rb" if binario else "r"
    with open(caminho, modo) as arquivo:
        return arquivo.read()

Ao chamar carregar("dados.bin", binario=True), o analisador sabe que o retorno é bytes. Sem Literal, ele precisaria tratar sempre str | bytes.

Variáveis e inferência

Um literal escrito diretamente costuma ser inferido de forma específica em alguns contextos. Já uma variável pode ser ampliada para str.

formato = "json"
exportar([], formato)  # alguns verificadores podem inferir str

formato_exato: Formato = "json"
exportar([], formato_exato)

Use anotação explícita quando precisar preservar o tipo literal. Final também pode ajudar em constantes.

from typing import Final

FORMATO_PADRAO: Final = "json"

Discriminando TypedDict

Literal combina muito bem com TypedDict. Uma chave discriminadora permite que o analisador identifique a variante correta.

from typing import Literal, TypedDict

class EventoCriado(TypedDict):
    tipo: Literal["criado"]
    id: int

class EventoErro(TypedDict):
    tipo: Literal["erro"]
    mensagem: str

Evento = EventoCriado | EventoErro


def processar(evento: Evento) -> str:
    if evento["tipo"] == "criado":
        return f"ID {evento['id']}"
    return evento["mensagem"]

Dentro de cada ramo, o tipo é refinado automaticamente.

Literal e match/case

Pattern matching fica mais seguro quando o tipo discrimina os casos.

Comando = Literal["iniciar", "parar", "status"]


def executar(comando: Comando) -> str:
    match comando:
        case "iniciar":
            return "iniciado"
        case "parar":
            return "parado"
        case "status":
            return "ativo"

Alguns analisadores detectam casos impossíveis ou ausência de cobertura, especialmente quando combinados com funções auxiliares que marcam caminhos inalcançáveis.

assert_never e exaustividade

assert_never() ajuda a garantir que todos os valores foram tratados.

from typing import assert_never


def executar(comando: Comando) -> str:
    if comando == "iniciar":
        return "iniciado"
    if comando == "parar":
        return "parado"
    if comando == "status":
        return "ativo"
    assert_never(comando)

Se um novo valor for adicionado ao alias, o analisador pode apontar o ramo final como alcançável.

Literal com Enum

Para conjuntos pequenos usados apenas como parâmetros, Literal é leve. Quando os valores precisam de métodos, nomes, iteração ou integração rica, Enum pode ser mais apropriado.

from enum import Enum

class FormatoEnum(str, Enum):
    JSON = "json"
    CSV = "csv"

Literal e Enum não são concorrentes absolutos. É possível usar membros de enum em anotações específicas, mas a escolha deve priorizar clareza. O artigo sobre enums em Python aprofunda o tema.

Valores numéricos e sentinelas

Literal pode representar códigos ou sentinelas.

StatusHTTP = Literal[200, 201, 204, 400, 404, 500]
Sentinela = Literal["AUTO", "PADRAO"]

Não use Literal para copiar listas enormes de códigos que mudam frequentemente. Nesses casos, um enum, classe de valor ou validação dinâmica pode ser mais sustentável.

LiteralString não é Literal

LiteralString é outro recurso de typing. Ele representa strings construídas a partir de literais confiáveis e foi criado para APIs sensíveis a injeção, como SQL. Não significa uma lista fechada de textos.

from typing import LiteralString


def executar_sql(consulta: LiteralString) -> None:
    ...

Use Literal["a", "b"] para opções exatas e LiteralString para restringir a origem de uma string em verificadores compatíveis.

Validação em runtime

Literal não rejeita valores durante a execução. Uma API pública ainda deve validar.

FORMATOS = {"json", "csv", "xml"}


def exportar_seguro(dados: list[dict], formato: str) -> bytes:
    if formato not in FORMATOS:
        raise ValueError(f"formato inválido: {formato}")
    ...

Uma estratégia comum é validar a string externa e então passá-la para uma camada interna tipada. Bibliotecas como Pydantic também entendem Literal e podem gerar validação e schema.

APIs públicas e compatibilidade

Adicionar um novo valor a um Literal parece uma mudança compatível, mas consumidores com checagem exaustiva podem precisar de atualização. Remover ou renomear valores é claramente incompatível.

Documente a semântica de cada opção, não apenas o nome. Dois valores podem ter o mesmo tipo, mas efeitos muito diferentes.

Erros comuns

  • Usar Literal para dados dinâmicos: ele funciona melhor com conjuntos pequenos e estáveis.
  • Confiar nele em runtime: entradas externas ainda precisam de validação.
  • Duplicar listas em muitas assinaturas: crie aliases reutilizáveis.
  • Combinar dezenas de valores sem necessidade: considere Enum ou classe de configuração.
  • Ignorar inferência ampliada: anote variáveis quando o literal precisar ser preservado.
  • Usar cast para esconder dados inválidos: cast não valida o valor.

Exemplo completo: cliente de relatório

from typing import Literal, overload

Formato = Literal["texto", "bytes"]

@overload
def gerar_relatorio(*, formato: Literal["texto"]) -> str:
    ...

@overload
def gerar_relatorio(*, formato: Literal["bytes"]) -> bytes:
    ...

def gerar_relatorio(*, formato: Formato) -> str | bytes:
    conteudo = "resultado"
    if formato == "texto":
        return conteudo
    return conteudo.encode("utf-8")

texto = gerar_relatorio(formato="texto")
binario = gerar_relatorio(formato="bytes")

O editor conhece o retorno específico de cada chamada e oferece métodos apropriados sem casts.

Conclusão

typing.Literal expressa opções exatas no sistema de tipos. Ele melhora contratos, autocomplete, overloads, variantes discriminadas e checagem exaustiva.

A documentação oficial de Literal no módulo typing detalha valores aceitos e equivalência. Use Literal para conjuntos fechados e estáveis, mantenha validação em runtime para dados externos e escolha Enum quando o domínio precisar de comportamento próprio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TypedDict no Python: dicionários tipados

    Aprenda TypedDict no Python para definir dicionários tipados, campos opcionais, NotRequired, Required, APIs e variantes com segurança estática.

    Ler mais

    Tempo de leitura: 5 minutos
    28/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

    Protocol no Python: tipagem estrutural

    Aprenda typing.Protocol no Python para tipagem estrutural, contratos genéricos, callbacks, runtime_checkable, testes e baixo acoplamento.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview no Python: buffers sem cópia

    Aprenda memoryview no Python para acessar buffers sem cópia, criar slices, editar bytearray, usar cast, mmap, struct e sockets com

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc no Python: rastreie memória

    Aprenda tracemalloc no Python para medir picos, criar e comparar snapshots, filtrar alocações e diagnosticar crescimento de memória.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: comunique threads

    Aprenda queue no Python para comunicar threads com FIFO, LIFO, prioridade, backpressure, task_done, join, sentinelas e shutdown seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Flat lay of a complete toolset neatly organized in a workshop setting, essential for auto repair tasks.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    selectors no Python: vários sockets

    Aprenda selectors no Python para multiplexar sockets, controlar leitura e escrita parcial, buffers, timeouts, wakeup e backpressure.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026