dataclasses.KW_ONLY permite definir en una dataclass un punto a partir del cual los campos solo pueden proporcionarse por nombre. Esto hace que los constructores sean más legibles, evita llamadas ambiguas y facilita la evolución de APIs sin romper código existente. En lugar de depender de una larga secuencia posicional, puedes exigir que las opciones delicadas o secundarias aparezcan explícitamente.
En esta guía aprenderás cómo funciona KW_ONLY, en qué se diferencia de kw_only=True y cómo interactúa con herencia, valores predeterminados, __match_args__, pattern matching y dataclasses.replace. También veremos errores frecuentes y reglas prácticas para producción.
El problema de los argumentos posicionales
Imagina una clase de configuración con varios booleanos y números. Una llamada como Config('prod', True, False, 30) resulta difícil de interpretar sin consultar la firma. Intercambiar dos valores del mismo tipo puede no producir ninguna excepción, pero sí cambiar silenciosamente el comportamiento.
Una llamada con nombres es mucho más clara: Config('prod', debug=True, cache=False, timeout=30). El código expresa su intención y una futura reorganización de campos tiene menos posibilidades de introducir regresiones.
Cómo usar dataclasses.KW_ONLY
KW_ONLY es un marcador de tipo. Se declara un pseudocampo, normalmente llamado _, y todos los campos definidos después pasan a ser keyword-only.
from dataclasses import dataclass, KW_ONLY
@dataclass
class Servicio:
nombre: str
_: KW_ONLY
timeout: int = 30
reintentos: int = 3
debug: bool = False
api = Servicio('pagos', timeout=10, reintentos=5)
El marcador no se almacena en la instancia, no aparece en la representación y nunca recibe un valor. Su única función es separar los campos posicionales de los campos nombrados.
La llamada Servicio('pagos', 10, 5) genera TypeError porque timeout y reintentos deben enviarse por nombre.
KW_ONLY frente a kw_only=True
La opción @dataclass(kw_only=True) convierte todos los campos en keyword-only. Es apropiada cuando ningún argumento posicional mejora la API. KW_ONLY es más selectivo: conserva unos pocos campos esenciales como posicionales y obliga a nombrar el resto.
Una regla útil es dejar como posicionales únicamente los campos que identifican claramente al objeto y que difícilmente cambiarán. Flags, límites, políticas, credenciales y opciones futuras suelen ser mejores como keyword-only.
Campos obligatorios con nombre
Los campos posteriores al marcador pueden tener valores predeterminados, pero no es obligatorio. Un campo requerido sigue siendo requerido; simplemente debe indicarse por nombre.
@dataclass
class Conexion:
host: str
_: KW_ONLY
token: str
puerto: int = 443
verificar_tls: bool = True
c = Conexion('api.ejemplo.com', token='secreto')
Este patrón resulta especialmente útil para credenciales y opciones de seguridad. Etiquetas explícitas como token= o verificar_tls= reducen errores y facilitan la revisión del código.
Valores predeterminados y diseño
Los parámetros keyword-only no eliminan la necesidad de elegir buenos valores predeterminados. Deben ser seguros, previsibles y estar documentados. Evita valores que ejecuten trabajo, lean el entorno o compartan estado mutable. Para listas y diccionarios utiliza field(default_factory=...).
Cuando una clase acumula demasiadas opciones, conviene dividirla en dataclasses más pequeñas. KW_ONLY mejora la firma, pero no debe servir como excusa para un constructor con decenas de responsabilidades.
Herencia y orden final
Las dataclasses combinan los campos de clases base y derivadas. Por eso, con herencia la firma final puede ser menos evidente. Un marcador en la clase base define la organización de esa clase, mientras que la subclase puede introducir campos adicionales y sus propias reglas.
Comprueba la firma pública con inspect.signature:
from inspect import signature
print(signature(Servicio))
En una biblioteca, una prueba de firma ayuda a detectar cambios accidentales cuando se reordenan campos o se añade una nueva clase base.
Pattern matching y __match_args__
Los campos keyword-only no se incluyen en __match_args__. Por lo tanto, los patrones estructurales posicionales solo consideran los campos posicionales. Esto suele producir patrones más estables.
match api:
case Servicio(nombre):
print(nombre)
Para comprobar datos keyword-only, usa patrones por atributo: case Servicio(timeout=10). Esta forma es explícita y no depende del orden interno.
Uso de dataclasses.replace
dataclasses.replace funciona naturalmente con campos keyword-only porque los cambios se envían por nombre. Es especialmente útil con modelos inmutables declarados mediante frozen=True.
from dataclasses import replace
rapido = replace(api, timeout=5)
La función crea una nueva instancia, modifica el campo solicitado y conserva los demás valores. Así puedes realizar actualizaciones claras sin mutar el objeto original.
Ventajas en APIs públicas
Los parámetros posicionales forman parte del contrato público. Añadir un parámetro en medio de la firma puede romper llamadas antiguas o reinterpretarlas de manera peligrosa. Los campos opcionales keyword-only son más sencillos de añadir porque cada llamada los identifica por nombre.
Esto es importante en SDKs, bibliotecas internas, modelos de dominio, clientes de servicios y objetos de configuración que cambian con el tiempo. No sustituye al versionado semántico, pero reduce la cantidad de cambios que requieren una versión incompatible.
La validación sigue siendo necesaria
KW_ONLY controla la forma de la llamada, no la validez de los datos. Usa __post_init__ para las reglas del dominio.
@dataclass
class Tarea:
nombre: str
_: KW_ONLY
prioridad: int = 5
intentos: int = 3
def __post_init__(self):
if not 1 <= self.prioridad <= 10:
raise ValueError('prioridad debe estar entre 1 y 10')
if self.intentos < 0:
raise ValueError('intentos no puede ser negativo')
La firma queda clara y la validación protege el estado. Son técnicas complementarias.
Errores frecuentes
No añadas varios marcadores KW_ONLY en una misma dataclass. Un solo límite es suficiente. Usa _ como nombre para indicar que no se trata de un dato real. Tampoco conviertas en keyword-only cada identificador simple cuando una llamada posicional breve sería natural.
Otro error es tratar keyword-only como un mecanismo de seguridad. Mejora la claridad, pero no limpia entradas, no comprueba tipos en tiempo de ejecución y no protege secretos. La validación y el almacenamiento seguro siguen siendo responsabilidades separadas.
Cómo probar el contrato
Prueba llamadas correctas con nombres y fallos esperados cuando se usan posiciones. Verifica campos obligatorios, valores predeterminados, herencia, pattern matching y replace. En paquetes públicos, comprobar la firma evita cambios accidentales de compatibilidad.
Los analizadores estáticos también entienden los parámetros keyword-only generados por dataclasses. Ejecutar mypy o Pyright ayuda a detectar llamadas incorrectas antes de llegar a producción.
Lista de buenas prácticas
Mantén uno o dos campos realmente esenciales como posicionales. Convierte opciones y flags en keyword-only. Elige valores seguros. Valida reglas en __post_init__. Prefiere modelos compuestos pequeños a un constructor gigante. Documenta los campos requeridos y prueba la firma final.
Para datos externos con validación compleja, Pydantic puede ser más adecuado. Para registros tipados y ligeros dentro de una aplicación Python, las dataclasses siguen siendo una excelente opción de la biblioteca estándar.
Lecturas relacionadas
Continúa con nuestras guías sobre typing.override en Python, StrEnum en Python, SimpleNamespace en Python y types.new_class en Python. Juntas ayudan a diseñar modelos y contratos más claros.
Consulta también la documentación oficial de dataclasses y la PEP 557.
Conclusión
dataclasses.KW_ONLY es una función pequeña con un gran efecto sobre legibilidad y compatibilidad. Conserva argumentos posicionales donde resultan naturales y exige nombres para opciones que podrían confundirse. En proyectos que evolucionan, esta separación reduce errores, mejora las revisiones y permite añadir nuevos campos opcionales con menor riesgo. Combínala con validación, pruebas de firma, análisis de tipos y documentación clara.







