O recurso dataclasses.KW_ONLY permite definir, em uma dataclass, um ponto a partir do qual os campos só podem ser informados por nome. Isso torna construtores mais claros, reduz chamadas ambíguas e facilita a evolução de APIs sem quebrar código existente. Em vez de depender de uma longa sequência de argumentos posicionais, você pode exigir que parâmetros opcionais ou sensíveis sejam escritos explicitamente.
Neste guia, você vai entender como KW_ONLY funciona, quando ele é melhor que kw_only=True, como ele interage com herança, valores padrão, __match_args__ e dataclasses.replace, além de conhecer erros comuns e padrões úteis para código de produção.
O problema dos argumentos posicionais
Considere uma classe de configuração com vários campos booleanos e números. Uma chamada como Config('prod', True, False, 30) é difícil de ler. Quem revisa o código precisa consultar a assinatura para descobrir o significado de cada valor. Pior: trocar dois argumentos do mesmo tipo pode não gerar erro, mas alterar completamente o comportamento.
Com campos somente por palavra-chave, a chamada fica explícita: Config('prod', debug=True, cache=False, timeout=30). O código comunica intenção, e mudanças futuras na ordem dos campos têm menos chance de causar regressões silenciosas.
Como usar dataclasses.KW_ONLY
KW_ONLY é um marcador de tipo. Você declara um campo fictício, normalmente chamado _, e todos os campos definidos depois dele passam a ser keyword-only.
from dataclasses import dataclass, KW_ONLY
@dataclass
class Servico:
nome: str
_: KW_ONLY
timeout: int = 30
retries: int = 3
debug: bool = False
api = Servico('pagamentos', timeout=10, retries=5)
O campo marcador não aparece como atributo da instância, não entra na representação e não precisa receber valor. Seu papel é apenas separar os campos posicionais dos campos nomeados.
Tentar chamar Servico('pagamentos', 10, 5) gera TypeError, pois timeout e retries devem ser fornecidos pelo nome.
KW_ONLY versus kw_only=True
A opção @dataclass(kw_only=True) torna todos os campos da classe somente por palavra-chave. É adequada quando nenhum argumento posicional faz sentido. Já KW_ONLY é mais seletivo: mantém alguns campos essenciais como posicionais e exige nomes apenas para os demais.
Uma regra prática é deixar posicionais somente os campos que identificam claramente o objeto e dificilmente mudarão. Configurações, flags, limites, políticas e opções futuras costumam ser melhores como keyword-only.
Valores padrão e clareza da assinatura
Campos após o marcador podem ter ou não valor padrão. Um campo obrigatório continua obrigatório, mas precisa ser nomeado.
@dataclass
class Conexao:
host: str
_: KW_ONLY
token: str
porta: int = 443
verificar_tls: bool = True
c = Conexao('api.exemplo.com', token='segredo')
Esse padrão é especialmente útil para credenciais e parâmetros de segurança. A presença explícita de token= ou verificar_tls= reduz erros em chamadas extensas.
Herança e ordem dos campos
Dataclasses combinam campos das classes base e derivadas. Por isso, a ordem final da assinatura deve ser verificada quando há herança. Um marcador na classe base afeta a definição daquela classe, mas uma subclasse pode adicionar novos campos e regras próprias. Teste a assinatura pública com inspect.signature para garantir que o construtor final esteja como esperado.
from inspect import signature
print(signature(Servico))
Em bibliotecas, vale incluir esse teste para evitar que uma refatoração altere acidentalmente quais parâmetros são posicionais.
Pattern matching e __match_args__
Campos keyword-only não são incluídos em __match_args__. Isso significa que o pattern matching posicional considera apenas os campos posicionais. Esse comportamento é positivo porque mantém padrões estruturais mais estáveis.
match api:
case Servico(nome):
print(nome)
Para testar campos nomeados, use padrões por atributo: case Servico(timeout=10). Essa forma é mais explícita e continua funcionando mesmo quando a ordem interna muda.
dataclasses.replace e cópias seguras
dataclasses.replace trabalha naturalmente com campos keyword-only, pois as alterações são passadas por nome. Isso combina bem com modelos imutáveis criados com frozen=True.
from dataclasses import replace
rapido = replace(api, timeout=5)
O resultado é uma nova instância com o campo modificado, preservando os demais valores e mantendo a legibilidade.
Quando usar em APIs públicas
Em uma API pública, argumentos posicionais fazem parte do contrato. Inserir um novo parâmetro no meio da assinatura pode quebrar chamadas antigas. Com keyword-only, novos campos opcionais podem ser adicionados com muito menos risco, desde que tenham valores padrão adequados.
Esse benefício é importante em SDKs, bibliotecas internas, modelos de configuração, objetos de domínio e serviços que mudam ao longo do tempo. O recurso não elimina a necessidade de versionamento, mas reduz a superfície de incompatibilidade.
Erros comuns
O primeiro erro é usar vários marcadores KW_ONLY na mesma dataclass. Apenas um é necessário. O segundo é escolher um nome relevante para o marcador; use _ para deixar claro que não é um campo real. O terceiro é tornar keyword-only um identificador que todos esperam passar posicionalmente, criando uma API excessivamente verbosa.
Outro erro é confiar apenas na dataclass para validação. KW_ONLY controla a forma da chamada, mas não valida conteúdo. Use __post_init__ para regras como timeout positivo, limites de tentativas ou combinações inválidas.
Exemplo com validação
@dataclass
class Job:
nome: str
_: KW_ONLY
prioridade: int = 5
tentativas: int = 3
def __post_init__(self):
if not 1 <= self.prioridade <= 10:
raise ValueError('prioridade deve estar entre 1 e 10')
if self.tentativas < 0:
raise ValueError('tentativas não pode ser negativo')
A assinatura fica clara e a validação protege o estado. As duas técnicas resolvem problemas diferentes e se complementam.
Boas práticas
Mantenha poucos campos posicionais, normalmente um ou dois. Use nomes descritivos para opções. Forneça padrões seguros. Documente campos obrigatórios. Teste a assinatura e as mensagens de erro. Em modelos grandes, considere separar configurações em dataclasses menores em vez de criar um construtor com dezenas de parâmetros.
Também compare com outras ferramentas. Para dados validados externamente, bibliotecas como Pydantic podem ser mais apropriadas. Para estruturas simples e tipadas, dataclasses continuam leves e integradas à biblioteca padrão.
Leituras relacionadas
Veja também nossos guias sobre typing.override no Python, StrEnum no Python, SimpleNamespace no Python e types.new_class no Python. Eles ajudam a projetar modelos, contratos e APIs mais previsíveis.
Consulte ainda a documentação oficial de dataclasses e a PEP 557, que descreve a motivação e o design das dataclasses.
Conclusão
dataclasses.KW_ONLY é um recurso pequeno com grande impacto na legibilidade e estabilidade de APIs. Ele permite preservar argumentos posicionais realmente essenciais e exigir nomes para opções que poderiam ser confundidas. Em projetos que evoluem continuamente, essa separação reduz erros, melhora revisões de código e facilita a inclusão de novos campos. Use-o principalmente em configurações, flags e parâmetros opcionais, combinado com validação, testes de assinatura e documentação clara.







