typing.Unpack representa la expansión estática de una estructura de tipos. Tiene dos usos principales: describir argumentos nombrados recibidos mediante **kwargs a partir de un TypedDict y expandir una tupla variádica basada en TypeVarTuple. En ambos casos, Unpack permite que el analizador vea elementos individuales ocultos dentro de una colección genérica.
Esta guía cubre kwargs tipados, opciones obligatorias y opcionales, wrappers, métodos, factories, protocolos invocables, TypeVarTuple, tuplas variádicas, genéricos que conservan formas, limitaciones de runtime y compatibilidad.
El problema de **kwargs genérico
def conectar(**opciones: object) -> None:
...
conectar(host="localhost", puerto=5432, ssl=True)La firma acepta cualquier nombre y cualquier valor. El editor no sugiere opciones, los errores de escritura parecen válidos y los tipos incorrectos son difíciles de detectar.
TypedDict con Unpack
from typing import TypedDict, Unpack
class OpcionesConexion(TypedDict):
host: str
puerto: int
def conectar(**opciones: Unpack[OpcionesConexion]) -> None:
host = opciones["host"]
puerto = opciones["puerto"]host y puerto son argumentos nombrados obligatorios. El analizador conoce sus tipos y rechaza nombres desconocidos.
Opciones opcionales
from typing import NotRequired
class OpcionesConexion(TypedDict):
host: str
puerto: int
timeout: NotRequired[float]
ssl: NotRequired[bool]Required y NotRequired determinan qué kwargs debe proporcionar el consumidor. Consulta Required y NotRequired en Python.
Llamadas válidas e inválidas
conectar(host="db.local", puerto=5432)
conectar(host="db.local", puerto=5432, timeout=3.0)
conectar(host="db.local") # falta puerto
conectar(host="db.local", puerto="5432") # tipo incorrecto
conectar(host="db.local", puerto=5432, reintentos=2) # nombre extraEstas comprobaciones son estáticas. En runtime, opciones sigue siendo un diccionario normal.
Dentro de la función
def conectar(**opciones: Unpack[OpcionesConexion]) -> None:
timeout = opciones.get("timeout", 5.0)
usar_ssl = opciones.get("ssl", False)Las claves obligatorias se acceden directamente. Las opcionales necesitan prueba, get() o un valor predeterminado.
Reenviar kwargs
def conectar_con_log(**opciones: Unpack[OpcionesConexion]) -> None:
print("conectando")
conectar(**opciones)El wrapper conserva nombres y tipos. Anotarlo como **opciones: object perdería la relación.
Añadir parámetros explícitos
def conectar_con_log(
nivel: str,
**opciones: Unpack[OpcionesConexion],
) -> None:
...Los parámetros normales pueden aparecer antes. Evita declarar un parámetro cuyo nombre también exista en el TypedDict.
Conflictos de nombres
class Opciones(TypedDict):
nivel: str
# No combines nivel explícito con Unpack[Opciones]Cada keyword debe aparecer una sola vez en la firma efectiva. Los analizadores deberían detectar solapamientos.
kwargs extra arbitrarios
Unpack de TypedDict normalmente describe un conjunto cerrado. Si la función acepta nombres adicionales, tal vez TypedDict no represente la API. Considera un mapping de metadatos, overloads, un objeto de configuración o un parámetro separado para extras.
Protocolos invocables
from typing import Protocol
class DatosEvento(TypedDict):
usuario_id: int
accion: str
class CallbackEvento(Protocol):
def __call__(self, **datos: Unpack[DatosEvento]) -> None: ...Un Protocol puede expresar un callback con argumentos nombrados específicos. Las implementaciones deben ofrecer una firma compatible.
Unpack en métodos
class Cliente:
def solicitar(self, **opciones: Unpack[OpcionesConexion]) -> None:
...El comportamiento es el mismo. self está separado del conjunto expandido.
Factories de objetos
class Configuracion(TypedDict, total=False):
cache: bool
timeout: float
class Servicio:
def __init__(self, nombre: str, **config: Unpack[Configuracion]) -> None:
...Esto ofrece autocompletado sin una lista extensa. Los parámetros explícitos suelen ser mejores cuando la API es pequeña y estable.
Herencia de TypedDict
class BaseHttp(TypedDict, total=False):
timeout: float
headers: dict[str, str]
class OpcionesGet(BaseHttp, total=False):
params: dict[str, str]Unpack[OpcionesGet] incluye claves heredadas. Las jerarquías profundas dificultan descubrir la firma real.
ReadOnly y kwargs
ReadOnly describe escritura sobre campos de TypedDict, pero los kwargs se construyen en cada llamada. Suele ser más útil en registros persistentes que en bolsas de opciones.
Unpack con TypeVarTuple
from typing import Generic, TypeVarTuple, Unpack
Dimensiones = TypeVarTuple("Dimensiones")
class Array(Generic[Unpack[Dimensiones]]):
...Las especializaciones pueden contener cantidades diferentes de dimensiones:
imagen: Array[int, int, int]
matriz: Array[int, int]Los argumentos representan una tupla de tipos expandida, no un único tipo tuple.
Sintaxis con estrella
class Array[*Dimensiones]:
...Python moderno permite esta forma en algunos contextos. Unpack[Dimensiones] sigue siendo importante para compatibilidad.
Tuplas variádicas
Ts = TypeVarTuple("Ts")
def agregar_prefijo(
valores: tuple[Unpack[Ts]],
) -> tuple[str, Unpack[Ts]]:
return ("prefijo", *valores)La función conserva cada tipo posicional de la tupla original y añade una string al principio.
Conservar formas
Shape = TypeVarTuple("Shape")
class Tensor(Generic[Unpack[Shape]]):
...
def batch(x: Tensor[Unpack[Shape]]) -> Tensor[int, Unpack[Shape]]:
...Bibliotecas numéricas pueden modelar operaciones que conservan o añaden ejes. Es una relación estática y no valida tamaños numéricos en runtime.
Un grupo variádico por lista
Varios TypeVarTuple sin restricciones serían ambiguos. El analizador no sabría cómo dividir los argumentos. Diseña prefijos y sufijos fijos alrededor de un solo grupo variádico.
TypeVarTuple frente a tuple[T, …]
tuple[T, ...] representa cantidad variable de valores del mismo tipo. TypeVarTuple conserva tipos posicionales distintos:
tuple[int, str, bytes]
# Ts puede representar (int, str, bytes)Unpack no trabaja en runtime
La anotación no desempaca datos ni valida argumentos. Los operadores normales ** y * realizan la expansión. Unpack solo describe esa relación a herramientas estáticas.
Compatibilidad
Usa typing_extensions.Unpack y TypeVarTuple en versiones anteriores. Comprueba también la versión del analizador porque el soporte de genéricos variádicos evoluciona.
Errores comunes
- Anotar **kwargs con TypedDict sin Unpack: significaría que cada valor es un TypedDict.
- Esperar validación de runtime: kwargs sigue siendo dict.
- Aceptar nombres arbitrarios sin modelarlos: Unpack describe claves conocidas.
- Solapar nombres explícitos y expandidos: un keyword no puede aparecer dos veces.
- Confundir TypeVarTuple con tupla homogénea: conserva posiciones diferentes.
- Usar variádicos en APIs simples: los parámetros explícitos pueden ser más claros.
Ejemplo completo: cliente HTTP
from typing import TypedDict, Unpack
class OpcionesHttp(TypedDict, total=False):
timeout: float
headers: dict[str, str]
seguir_redirects: bool
reintentos: int
def get(
url: str,
**opciones: Unpack[OpcionesHttp],
) -> bytes:
timeout = opciones.get("timeout", 10.0)
headers = opciones.get("headers", {})
seguir = opciones.get("seguir_redirects", True)
reintentos = opciones.get("reintentos", 1)
return realizar_get(url, timeout, headers, seguir, reintentos)El editor sugiere cuatro nombres, rechaza errores y conserva tipos. La implementación recibe un diccionario normal y aplica valores predeterminados.
Cuándo evitar Unpack
Prefiere parámetros explícitos cuando hay pocas opciones estables. Usa una dataclass de configuración cuando se comparten o necesitan validación. Usa Unpack cuando la API es naturalmente kwargs y quieres describirla con precisión.
Conclusión
typing.Unpack hace visibles las expansiones al sistema de tipos. Con TypedDict crea kwargs con nombres, tipos y presencia conocidos. Con TypeVarTuple habilita genéricos con cantidad variable de parámetros posicionales.
La documentación oficial de Unpack en Python define las formas soportadas. Úsalo para conservar firmas y relaciones variádicas, manteniendo validación de runtime y eligiendo APIs más simples cuando sean suficientes.







