collections.UserDict é uma classe auxiliar para criar dicionários personalizados por composição. Em vez de depender diretamente da implementação interna de dict, ela armazena os dados em um atributo chamado data e encaminha as operações de mapeamento para esse objeto.
Essa estrutura facilita validação de chaves, normalização de valores, logging, controle de acesso e criação de APIs previsíveis. Também reduz algumas surpresas que podem ocorrer ao sobrescrever métodos de uma subclasse direta de dict.
Primeiro exemplo
from collections import UserDict
class ChavesTexto(UserDict):
def __setitem__(self, chave, valor):
if not isinstance(chave, str):
raise TypeError("a chave deve ser texto")
super().__setitem__(chave, valor)
dados = ChavesTexto()
dados["nome"] = "Ada"
A validação fica centralizada em __setitem__. Operações como update tendem a respeitar o comportamento personalizado porque a classe foi projetada para extensibilidade.
O atributo data
config = ChavesTexto({"modo": "produção"})
print(config.data)
data contém o dicionário real. Use-o com cuidado: modificar esse atributo diretamente pode ignorar validações implementadas em métodos públicos.
Normalizando chaves
class DicionarioCasefold(UserDict):
def __setitem__(self, chave, valor):
super().__setitem__(str(chave).casefold(), valor)
def __getitem__(self, chave):
return super().__getitem__(str(chave).casefold())
def __contains__(self, chave):
return super().__contains__(str(chave).casefold())
Quando uma chave é normalizada, aplique a mesma regra em leitura, escrita, remoção e teste de presença.
Valores validados
class Pontuacoes(UserDict):
def __setitem__(self, jogador, pontos):
pontos = int(pontos)
if pontos < 0:
raise ValueError("pontuação negativa")
super().__setitem__(jogador, pontos)
Evite coerções silenciosas que possam esconder erros. Documente claramente quais conversões são permitidas.
Valor padrão com __missing__
class Contadores(UserDict):
def __missing__(self, chave):
return 0
__missing__ é acionado por __getitem__, não necessariamente por get ou testes de presença. Se você precisa inserir automaticamente valores, defaultdict pode ser mais apropriado.
UserDict versus dict
Herdar diretamente de dict pode oferecer desempenho um pouco melhor e integração natural com APIs que exigem o tipo concreto. UserDict oferece uma superfície mais simples para personalização, porque muitas operações passam pelos métodos sobrescritos.
UserDict versus MutableMapping
Implementar collections.abc.MutableMapping é útil quando os dados não são guardados em um dicionário comum, por exemplo em banco de dados, cache remoto ou estrutura compacta. Nesse caso, você implementa os métodos fundamentais. UserDict é melhor quando um dicionário interno resolve o armazenamento.
Cópias
Teste copy, deepcopy e o construtor da classe. Atributos adicionais podem exigir uma implementação própria para serem preservados.
class Config(UserDict):
def __init__(self, *args, origem=None, **kwargs):
self.origem = origem
super().__init__(*args, **kwargs)
Serialização
Muitas bibliotecas JSON aceitam apenas dict concreto. Converta explicitamente quando necessário:
import json
texto = json.dumps(dict(config), ensure_ascii=False)
Imutabilidade parcial
Você pode bloquear alterações depois de uma fase de configuração, sobrescrevendo métodos mutáveis. Porém, para uma visão realmente somente leitura, considere types.MappingProxyType, apresentado no guia interno sobre MappingProxyType.
Erros comuns
- Modificar
datadiretamente e ignorar validações. - Normalizar somente em
__setitem__. - Esquecer métodos de remoção e atualização.
- Assumir que toda biblioteca aceita qualquer Mapping.
- Adicionar efeitos colaterais surpreendentes a operações simples.
Boas práticas
Mantenha invariantes pequenas e claras, use super(), escreva testes para todas as operações mutáveis e exponha métodos de domínio quando uma alteração exigir regras complexas. Veja também os artigos internos sobre dicionários e collections.
Conclusão
UserDict é uma base prática para mappings personalizados que ainda usam um dicionário comum como armazenamento. Ele favorece composição e torna validação, normalização e instrumentação mais previsíveis.







