typing.TypeVarTuple representa una cantidad variable de parámetros de tipo. Un TypeVar normal captura un solo tipo, mientras TypeVarTuple puede capturar una secuencia heterogénea como (int, str, bytes) y reutilizarla en otra posición. Esto habilita genéricos variádicos, transformaciones de tuplas que conservan posiciones y modelos de arrays con forma.
Esta guía cubre declaraciones, Unpack, sintaxis con estrella, prefijos y sufijos, clases genéricas, dimensiones, límites de inferencia, comparación con ParamSpec, runtime, introspección, compatibilidad y pruebas.
El límite de TypeVar
from typing import TypeVar
T = TypeVar("T")
def repetir(valor: T) -> tuple[T, T]:
return (valor, valor)TypeVar conserva un tipo. No puede capturar una cantidad desconocida de posiciones con tipos diferentes.
Tuplas homogéneas
def contar(valores: tuple[int, ...]) -> int:
return len(valores)tuple[int, ...] acepta cualquier cantidad de enteros. Todas las posiciones comparten tipo. No conserva tuple[int, str, bool] como tres tipos separados.
Declarar TypeVarTuple
from typing import TypeVarTuple
Ts = TypeVarTuple("Ts")Ts representa cero o más tipos posicionales. Debe expandirse al usarlo.
Expandir con Unpack
from typing import Unpack
def identidad_tupla(
valores: tuple[Unpack[Ts]],
) -> tuple[Unpack[Ts]]:
return valoresUna entrada tuple[int, str] produce el mismo tipo de salida. Cada posición se conserva.
Sintaxis con estrella
def identidad_tupla(valores: tuple[*Ts]) -> tuple[*Ts]:
return valoresEn contextos modernos, *Ts equivale a Unpack[Ts]. La forma explícita sigue siendo útil para compatibilidad.
Añadir prefijo
def con_nombre(
valores: tuple[*Ts],
) -> tuple[str, *Ts]:
return ("registro", *valores)Una entrada tuple[int, bool] se convierte en tuple[str, int, bool].
Añadir sufijo
def con_estado(
valores: tuple[*Ts],
) -> tuple[*Ts, bool]:
return (*valores, True)Los elementos fijos alrededor del grupo modelan transformaciones uniformes.
Eliminar la primera posición
def cola(
valores: tuple[object, *Ts],
) -> tuple[*Ts]:
primero, *resto = valores
return tuple(resto)La anotación exige al menos un elemento. Algunos analizadores pueden necesitar ayuda para relacionar la lista intermedia con la tupla variádica.
Clases genéricas variádicas
from typing import Generic
class Registro(Generic[*Ts]):
def __init__(self, valores: tuple[*Ts]) -> None:
self.valores = valoresLas especializaciones pueden tener números diferentes de parámetros:
fila: Registro[int, str]
pixel: Registro[int, int, int, float]La clase conserva la estructura posicional de cada instancia.
Relaciones tipo zip
Una función zip completamente genérica relaciona varios iterables con una tupla de salida. TypeVarTuple expresa parte de esa relación, pero la inferencia de iterables individuales puede requerir overloads.
Modelar formas
Shape = TypeVarTuple("Shape")
class Array(Generic[*Shape]):
...Una matriz puede ser Array[Alto, Ancho] y una imagen Array[Alto, Ancho, Canales]. Son marcadores de tipo, no tamaños numéricos de runtime.
Añadir dimensión batch
class Batch: ...
def agregar_batch(x: Array[*Shape]) -> Array[Batch, *Shape]:
...La operación conserva todas las dimensiones existentes y añade una al principio.
Límites de transposición
TypeVarTuple conserva una secuencia, pero no ofrece inversión o permutación general a nivel de tipos. Para matrices 2D, parámetros explícitos u overloads pueden ser más claros.
Un TypeVarTuple por lista
# Ambiguo: tuple[*As, *Bs]El analizador no sabría dónde termina el primer grupo. Usa un solo grupo con elementos fijos alrededor.
El grupo puede estar vacío
TypeVarTuple puede capturar cero tipos. Si la API requiere al menos una posición, añade un prefijo o sufijo fijo en la anotación.
Restricciones
TypeVarTuple no ofrece exactamente los mismos bounds y constraints por elemento que TypeVar. Si todos los elementos deben compartir una interfaz, una tupla homogénea puede ser mejor.
TypeVarTuple y Unpack
TypeVarTuple define el grupo y Unpack lo expande. Consulta Unpack en Python para kwargs tipados y otras expansiones.
Inferencia de literales
resultado = identidad_tupla((1, "a", True))El analizador puede inferir tuple[int, str, bool]. Usa assert_type() para proteger la expectativa.
Las listas no conservan posiciones
Una lista suele tener un tipo común como list[int | str]. No conserva un tipo diferente por índice. TypeVarTuple encaja mejor con tuplas y listas de parámetros genéricos.
Parámetros de Callable
TypeVarTuple no sustituye ParamSpec. ParamSpec conserva nombres, categorías y kwargs de funciones. TypeVarTuple conserva una secuencia posicional de tipos.
Comparación con overloads
Sin TypeVarTuple, una biblioteca podría escribir overloads para tuplas de una, dos, tres y cuatro posiciones. El grupo variádico elimina repetición cuando la transformación es uniforme.
Runtime
TypeVarTuple no valida longitudes ni valores durante ejecución. Las clases deben imponer invariantes normalmente. Los argumentos genéricos pueden borrarse o estar disponibles parcialmente.
Introspección
get_origin() y get_args() ayudan a examinar especializaciones, pero las instancias no siempre conservan toda la información. No bases seguridad en metadatos de typing.
Compatibilidad
Usa typing_extensions.TypeVarTuple y Unpack en versiones anteriores. El soporte del analizador también importa; prueba con versiones modernas de mypy o pyright.
Errores comunes
- Usar TypeVarTuple sin expandir: escribe
*TsoUnpack[Ts]. - Confundir con tuple[T, …]: conserva tipos distintos por posición.
- Crear dos grupos variádicos: la división es ambigua.
- Esperar validación de forma en runtime: la relación es estática.
- Reemplazar ParamSpec: las firmas necesitan más información.
- Modelar más de lo soportado: simplifica la API.
Ejemplo completo: pipeline de registros
from typing import Generic, TypeVarTuple
Campos = TypeVarTuple("Campos")
class Fila(Generic[*Campos]):
def __init__(self, datos: tuple[*Campos]) -> None:
self.datos = datos
def numerar(fila: Fila[*Campos]) -> Fila[int, *Campos]:
return Fila((1, *fila.datos))
def marcar(fila: Fila[*Campos]) -> Fila[*Campos, bool]:
return Fila((*fila.datos, True))
entrada = Fila(("Ana", 42.0))
numerada = numerar(entrada)
marcada = marcar(numerada)El tipo evoluciona de Fila[str, float] a Fila[int, str, float] y luego Fila[int, str, float, bool].
Pruebas estáticas
from typing import assert_type
assert_type(numerada.datos, tuple[int, str, float])
assert_type(marcada.datos, tuple[int, str, float, bool])Estas pruebas protegen la inferencia durante cambios.
Cuándo evitar TypeVarTuple
Usa dataclass o NamedTuple cuando los campos tienen nombres estables. Usa tupla homogénea cuando comparten tipo. Usa ParamSpec para wrappers de función. Usa TypeVarTuple cuando varía el número de posiciones y cada tipo debe conservarse.
Conclusión
TypeVarTuple extiende los genéricos a secuencias variables de tipos. Conserva tuplas heterogéneas, crea clases con varios parámetros y modela transformaciones de forma sin decenas de overloads.
La documentación oficial de TypeVarTuple en Python define las reglas. Expándelo con Unpack o estrella, mantén un grupo variádico por lista y verifica el comportamiento con pruebas estáticas.







