MappingProxyType: dicionário só leitura

Publicado em: 28/08/2026
Tempo de leitura: 6 minutos
Black and white close-up of a dictionary page showing the definition of 'virus.'

Às vezes uma aplicação precisa compartilhar um dicionário sem permitir que o consumidor altere sua estrutura diretamente. Copiar o objeto pode ser caro ou esconder atualizações feitas pelo proprietário. Expor o dicionário original, por outro lado, permite mudanças acidentais. types.MappingProxyType cria uma visão dinâmica e somente leitura de um mapping existente.

Neste guia, você aprenderá a criar proxies, entender a diferença entre imutabilidade e acesso somente leitura, compartilhar configurações, expor registros internos, trabalhar com objetos mutáveis aninhados, comparar com cópias e bibliotecas persistentes e evitar expectativas incorretas sobre segurança e concorrência.

O que é MappingProxyType?

MappingProxyType recebe um mapping e devolve um objeto que oferece operações de leitura, mas bloqueia alterações por meio do proxy.

from types import MappingProxyType

configuracao = {
    "host": "localhost",
    "porta": 8000,
}

publica = MappingProxyType(configuracao)

print(publica["host"])
# publica["porta"] = 9000  # TypeError

O proxy implementa a interface de mapping: acesso por chave, iteração, len(), get(), keys(), values() e items(). Métodos de mutação, como atribuição, update() e pop(), não estão disponíveis.

Uma visão dinâmica, não uma cópia

O proxy continua ligado ao dicionário original. Se o proprietário modificar o objeto base, a mudança aparece na visão.

origem = {"modo": "teste"}
visao = MappingProxyType(origem)

print(visao["modo"])  # teste
origem["modo"] = "producao"
print(visao["modo"])  # producao

Essa característica é central. MappingProxyType não congela o dicionário; ele controla por qual referência a mutação pode ocorrer.

Somente leitura não significa imutabilidade profunda

O proxy protege a estrutura externa, mas não transforma valores aninhados em objetos imutáveis.

dados = {
    "usuarios": ["Ana", "Bruno"],
    "opcoes": {"debug": False},
}

visao = MappingProxyType(dados)
visao["usuarios"].append("Carla")
visao["opcoes"]["debug"] = True

print(dados)

A lista e o dicionário internos continuam mutáveis. Para uma garantia profunda, normalize valores para tipos imutáveis, faça cópia profunda quando apropriado ou use estruturas persistentes projetadas para esse objetivo.

Expondo estado interno com segurança

Uma classe pode manter um dicionário mutável internamente e expor um proxy aos consumidores.

from collections.abc import Mapping
from types import MappingProxyType

class RegistroPlugins:
    def __init__(self) -> None:
        self._plugins: dict[str, object] = {}
        self._publico = MappingProxyType(self._plugins)

    @property
    def plugins(self) -> Mapping[str, object]:
        return self._publico

    def registrar(self, nome: str, plugin: object) -> None:
        if nome in self._plugins:
            raise ValueError(f"plugin duplicado: {nome}")
        self._plugins[nome] = plugin

O consumidor pode listar e consultar plugins, mas precisa usar registrar() para alterar o estado. Isso preserva validações e invariantes da classe.

Por que anotar como Mapping?

Funções que apenas leem dados devem aceitar collections.abc.Mapping em vez de dict.

from collections.abc import Mapping


def montar_url(config: Mapping[str, object]) -> str:
    host = str(config["host"])
    porta = int(config["porta"])
    return f"http://{host}:{porta}"

Assim, a função aceita dicionários, proxies e outros mappings. A anotação comunica que mutação não faz parte do contrato.

Configurações públicas e privadas

Um padrão comum é construir uma configuração mutável durante a inicialização e expor uma visão de leitura depois.

_config = {
    "timeout": 5.0,
    "retries": 3,
    "features": frozenset({"cache", "metricas"}),
}

CONFIG = MappingProxyType(_config)

O nome em maiúsculas sugere constante, enquanto o proxy impede atribuições acidentais por consumidores. Contudo, código que ainda possui _config pode alterar os valores.

MappingProxyType e atributos de classe

O próprio Python usa um tipo de proxy para expor o namespace de classes em MinhaClasse.__dict__. Isso impede modificações diretas que ignorariam os mecanismos internos de atualização de classes.

class Exemplo:
    valor = 10

print(type(Exemplo.__dict__))
print(Exemplo.__dict__["valor"])
# Exemplo.__dict__["valor"] = 20  # não permitido
Exemplo.valor = 20

A atribuição correta passa pelo objeto classe, permitindo que o runtime mantenha caches e invariantes.

Comparação com dict.copy()

Uma cópia rasa produz um dicionário independente no nível externo.

origem = {"a": 1}
copia = origem.copy()
proxy = MappingProxyType(origem)

origem["a"] = 2
print(copia["a"])  # 1
print(proxy["a"])  # 2

Use cópia quando o consumidor precisa de um snapshot independente. Use proxy quando deve observar atualizações do proprietário sem poder alterá-las pela referência exposta.

Snapshot somente leitura

Quando deseja estabilidade e proteção, combine cópia e proxy.

snapshot = MappingProxyType(dict(origem))

O proxy protege a cópia externa contra mutação, e mudanças posteriores na origem não aparecem. Valores aninhados ainda são compartilhados se a cópia for rasa.

Desempenho e memória

Criar um proxy é barato porque não duplica todas as entradas. Isso pode ser útil para mappings grandes expostos a muitos consumidores. O acesso adiciona uma camada pequena de indireção, normalmente irrelevante diante do benefício de encapsulamento.

Não escolha MappingProxyType apenas por micro-otimização. O principal valor é tornar a intenção de leitura explícita e impedir mutações acidentais.

Concorrência e thread safety

Um proxy não torna o dicionário thread-safe. Se outra thread modifica a origem enquanto uma iteração ocorre, o consumidor pode observar estados intermediários ou receber erros como “dictionary changed size during iteration”.

for chave, valor in proxy.items():
    ...  # a origem não deve mudar estruturalmente durante esta iteração

Para concorrência, use locks, snapshots, troca atômica de referências ou estruturas apropriadas. O proxy controla permissões da interface, não sincronização.

Serialização

Nem toda biblioteca aceita mappingproxy diretamente. Ao enviar para JSON ou APIs que exigem dict, converta explicitamente.

import json

texto = json.dumps(dict(proxy))

A conversão cria uma cópia rasa. Se os valores contêm tipos não serializáveis, ainda será necessário normalizá-los.

Hash e uso como chave

Um proxy de mapping não deve ser tratado automaticamente como um objeto profundamente imutável e hashable. Mesmo que uma versão específica ofereça determinadas operações, a origem pode mudar. Para chaves estáveis, prefira uma representação imutável, como tupla ordenada de pares quando os valores também forem hashable.

chave = tuple(sorted(origem.items()))

Erros comuns

  • Acreditar que os valores ficam congelados: objetos aninhados continuam mutáveis.
  • Esperar um snapshot: o proxy acompanha mudanças na origem.
  • Usar como mecanismo de segurança: quem possui a referência original pode alterar tudo.
  • Assumir thread safety: não há sincronização automática.
  • Anotar consumidores como dict: prefira Mapping quando a função só lê.
  • Converter repetidamente para dict: isso elimina a vantagem de evitar cópias.

Exemplo completo: catálogo versionado

from collections.abc import Mapping
from types import MappingProxyType

class Catalogo:
    def __init__(self) -> None:
        self._itens: dict[str, float] = {}
        self._versao = 0
        self._view = MappingProxyType(self._itens)

    @property
    def itens(self) -> Mapping[str, float]:
        return self._view

    @property
    def versao(self) -> int:
        return self._versao

    def definir_preco(self, codigo: str, preco: float) -> None:
        if preco < 0:
            raise ValueError("preço negativo")
        self._itens[codigo] = preco
        self._versao += 1

catalogo = Catalogo()
catalogo.definir_preco("A1", 39.90)
print(catalogo.itens["A1"])
# catalogo.itens["A1"] = 0  # TypeError

O catálogo mantém controle sobre atualizações, enquanto leitores observam o estado atual por uma interface não mutável.

Quando usar outra solução?

Use frozenset para conjuntos imutáveis, tuplas para sequências fixas, dataclasses congeladas para registros com atributos e bibliotecas de estruturas persistentes quando precisar de atualizações funcionais eficientes. Para configuração externa, valide e converta antes de expor.

Também é possível retornar uma cópia simples se o mapping for pequeno e o isolamento total for mais importante do que observar atualizações.

Conclusão

types.MappingProxyType cria uma visão dinâmica de somente leitura sobre um mapping. Ele é útil para encapsular dicionários internos, expor registros e configurações e comunicar que consumidores não devem alterar a estrutura.

A documentação oficial de MappingProxyType no módulo types descreve seu comportamento. Use o proxy sabendo que ele não oferece imutabilidade profunda, snapshot, segurança ou sincronização; essas garantias exigem decisões adicionais de arquitetura.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    A close-up of a padlock securing a wire fence, symbolizing protection and safety.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Literal no Python: restrinja valores

    Aprenda typing.Literal no Python para restringir valores, criar overloads, discriminar TypedDict, usar match/case e melhorar APIs tipadas.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    Close-up of hands typing on a laptop keyboard, Python book in sight, coding in progress.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    TypedDict no Python: dicionários tipados

    Aprenda TypedDict no Python para definir dicionários tipados, campos opcionais, NotRequired, Required, APIs e variantes com segurança estática.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    Protocol no Python: tipagem estrutural

    Aprenda typing.Protocol no Python para tipagem estrutural, contratos genéricos, callbacks, runtime_checkable, testes e baixo acoplamento.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview no Python: buffers sem cópia

    Aprenda memoryview no Python para acessar buffers sem cópia, criar slices, editar bytearray, usar cast, mmap, struct e sockets com

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc no Python: rastreie memória

    Aprenda tracemalloc no Python para medir picos, criar e comparar snapshots, filtrar alocações e diagnosticar crescimento de memória.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    queue no Python: comunique threads

    Aprenda queue no Python para comunicar threads com FIFO, LIFO, prioridade, backpressure, task_done, join, sentinelas e shutdown seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    28/08/2026