Literal en Python: restringe valores

Publicado el: 28/08/2026
Tempo de leitura: 4 minutos
Retro fuel pump with rusty metal and vintage design, featuring a nozzle and liter meter.

Una anotación como str indica que una función acepta texto, pero no especifica qué textos son válidos. Muchos parámetros admiten un conjunto cerrado como "json", "csv" o "xml". typing.Literal permite expresar esos valores exactos en la firma, mejorando autocompletado, documentación y análisis estático.

Esta guía explica Literal con strings, números, booleanos, overloads, discriminadores de TypedDict, match/case, aliases reutilizables, comprobación exhaustiva y validación en runtime.

Qué significa Literal

Literal describe un valor específico, no solo su tipo general.

from typing import Literal

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

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

exportar([], "json")
# exportar([], "yaml")  # error del analizador

Durante la ejecución, la anotación no bloquea valores inválidos. Las entradas externas siguen necesitando validación.

Cuándo mejora una API

Literal funciona bien cuando el conjunto de opciones es pequeño, estable y forma parte del contrato público. Sustituye strings mágicos por elecciones visibles para el editor.

ModoApertura = Literal["lectura", "escritura", "anexar"]


def abrir_recurso(ruta: str, modo: ModoApertura) -> None:
    ...

El editor puede sugerir todos los valores permitidos, algo más útil que un parámetro str acompañado solo por un comentario.

Strings, números y booleanos

Literal admite valores exactos compatibles con el sistema de typing.

Nivel = Literal[0, 1, 2, 3]
Direccion = Literal["norte", "sur", "este", "oeste"]
Activo = Literal[True]


def configurar(nivel: Nivel, direccion: Direccion, activo: Activo) -> None:
    ...

Literal[True] es más específico que bool. Puede ser útil en overloads, aunque muchos flags booleanos quedan más claros con funciones separadas o enums.

Aliases reutilizables

Crea un alias cuando el mismo conjunto aparezca en varias firmas.

from typing import Literal, TypeAlias

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


def log(mensaje: str, nivel: NivelLog = "info") -> None:
    ...

Los aliases evitan duplicación y ofrecen un único lugar para evolucionar el contrato.

Literal con overload

Uno de los usos más potentes es relacionar un valor exacto con el tipo retornado.

from typing import Literal, overload

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

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

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

Al llamar cargar("datos.bin", binario=True), el analizador sabe que el resultado es bytes. Sin Literal, todos los consumidores deberían manejar str | bytes.

Variables e inferencia ampliada

Un literal escrito directamente puede conservar un tipo específico, mientras una variable puede ampliarse a str.

formato = "json"
exportar([], formato)  # puede inferirse como str

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

Usa una anotación explícita cuando necesites preservar el tipo literal. Final también puede ayudar con constantes.

from typing import Final

FORMATO_PREDETERMINADO: Final = "json"

Discriminar variantes de TypedDict

Literal combina muy bien con TypedDict. Una clave discriminadora permite identificar la variante correcta.

from typing import Literal, TypedDict

class EventoCreado(TypedDict):
    tipo: Literal["creado"]
    id: int

class EventoError(TypedDict):
    tipo: Literal["error"]
    mensaje: str

Evento = EventoCreado | EventoError


def procesar(evento: Evento) -> str:
    if evento["tipo"] == "creado":
        return f"ID {evento['id']}"
    return evento["mensaje"]

Dentro de cada rama, la unión se reduce automáticamente a la forma correspondiente.

Literal y match/case

El pattern matching es más fácil de analizar cuando la entrada tiene un tipo literal cerrado.

Comando = Literal["iniciar", "detener", "estado"]


def ejecutar(comando: Comando) -> str:
    match comando:
        case "iniciar":
            return "iniciado"
        case "detener":
            return "detenido"
        case "estado":
            return "activo"

Las herramientas estáticas pueden detectar casos imposibles o falta de cobertura.

Exhaustividad con assert_never

assert_never() hace visibles los casos olvidados.

from typing import assert_never


def ejecutar(comando: Comando) -> str:
    if comando == "iniciar":
        return "iniciado"
    if comando == "detener":
        return "detenido"
    if comando == "estado":
        return "activo"
    assert_never(comando)

Si se añade un comando al alias, el analizador puede indicar que la última rama ahora es alcanzable.

Literal frente a Enum

Literal es ligero para conjuntos pequeños usados principalmente en firmas. Enum puede ser mejor cuando los valores necesitan métodos, nombres, iteración o comportamiento de dominio.

from enum import Enum

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

La guía sobre enums en Python explica la alternativa nominal. La elección debe mantener claras las llamadas y la validación.

Códigos numéricos y sentinelas

Literal puede describir un conjunto pequeño de códigos o valores centinela.

EstadoHTTP = Literal[200, 201, 204, 400, 404, 500]
Sentinela = Literal["AUTO", "DEFAULT"]

No copies registros enormes o cambiantes dentro de una anotación. Un enum, objeto de valor o búsqueda dinámica puede ser más sostenible.

LiteralString es diferente

LiteralString representa strings construidas a partir de literales confiables y está orientado a APIs sensibles a inyección. No significa una lista cerrada de textos.

from typing import LiteralString


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

Usa Literal["a", "b"] para opciones exactas y LiteralString para restringir el origen de una cadena.

Validación en runtime

Literal no rechaza valores durante la ejecución.

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


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

Un diseño común valida la cadena externa y luego la entrega a una capa interna tipada. Pydantic y otras bibliotecas también interpretan Literal para validar y generar schemas.

Compatibilidad de APIs públicas

Añadir un valor nuevo puede parecer compatible, pero consumidores con manejo exhaustivo quizá necesiten cambios. Eliminar o renombrar una opción es claramente incompatible.

Documenta la semántica de cada alternativa. La escritura exacta no explica efectos secundarios, rendimiento, orden o errores.

Errores comunes

  • Usar Literal para datos dinámicos: funciona mejor con conjuntos pequeños y estables.
  • Confiar en runtime: las entradas no confiables siguen necesitando validación.
  • Repetir listas largas: crea aliases.
  • Usar decenas de valores sin estructura: considera Enum o un objeto de configuración.
  • Ignorar inferencia ampliada: anota variables cuando importe la exactitud.
  • Aplicar cast a valores inválidos: cast no valida.

Ejemplo completo: salida de informes

from typing import Literal, overload

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

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

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

def generar_informe(*, salida: Salida) -> str | bytes:
    contenido = "resultado"
    if salida == "texto":
        return contenido
    return contenido.encode("utf-8")

texto = generar_informe(salida="texto")
binario = generar_informe(salida="bytes")

El editor conoce el resultado exacto de cada llamada y ofrece los métodos apropiados sin casts.

Conclusión

typing.Literal expresa elecciones exactas dentro del sistema de tipos. Mejora contratos, autocompletado, overloads, variantes discriminadas y manejo exhaustivo.

La documentación oficial de Literal en Python detalla los valores aceptados y su equivalencia. Úsalo para conjuntos cerrados y estables, conserva validación en runtime y elige Enum cuando el dominio necesite comportamiento propio.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    A monochrome image of a lens on an open dictionary page, highlighting words.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    MappingProxyType: diccionario solo lectura

    Aprende MappingProxyType en Python para exponer diccionarios de solo lectura, crear vistas dinámicas y snapshots, y proteger invariantes sin copias.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TypedDict en Python: diccionarios tipados

    Aprende TypedDict en Python para diccionarios tipados, claves opcionales, NotRequired, Required, payloads de APIs y variantes discriminadas.

    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 Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Protocol en Python: tipado estructural

    Aprende Python Protocol para tipado estructural, contratos genéricos, callbacks, runtime_checkable, pruebas e inyección de dependencias.

    Ler mais

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

    memoryview en Python: buffers sin copia

    Aprende memoryview en Python para buffers sin copia, slices, bytearray editable, cast, mmap, struct, sockets y control seguro del ciclo

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc en Python: rastrea memoria

    Aprende tracemalloc en Python para medir picos, crear y comparar snapshots, filtrar asignaciones y diagnosticar crecimiento de memoria.

    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 Avanzado
    Foto de perfil de Leandro Hirt da Academify

    queue en Python: comunica threads

    Aprende queue en Python para comunicar threads con FIFO, LIFO, prioridad, backpressure, task tracking, sentinelas y shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026