MappingProxyType: diccionario solo lectura

Publicado el: 28/08/2026
Tempo de leitura: 5 minutos
A monochrome image of a lens on an open dictionary page, highlighting words.

A veces una aplicación necesita compartir un diccionario sin permitir que los consumidores modifiquen su estructura directamente. Copiar el objeto puede ser costoso o puede ocultar actualizaciones realizadas por el propietario. Exponer el diccionario original permite cambios accidentales. types.MappingProxyType crea una vista dinámica de solo lectura sobre un mapping existente.

Esta guía explica cómo crear proxies, diferenciar acceso de solo lectura e inmutabilidad, exponer registros internos y configuración, manejar valores mutables anidados, comparar proxies con copias y snapshots, y evitar expectativas incorrectas sobre seguridad y concurrencia.

Qué hace MappingProxyType

MappingProxyType recibe un mapping y devuelve un objeto que permite operaciones de lectura, pero bloquea la mutación a través del proxy.

from types import MappingProxyType

configuracion = {
    "host": "localhost",
    "puerto": 8000,
}

publica = MappingProxyType(configuracion)

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

El proxy admite indexación, iteración, len(), get(), keys(), values() e items(). Los métodos de mutación no están disponibles.

Una vista dinámica, no una copia

El proxy permanece conectado al mapping original. Los cambios del propietario aparecen inmediatamente.

origen = {"modo": "prueba"}
vista = MappingProxyType(origen)

print(vista["modo"])  # prueba
origen["modo"] = "produccion"
print(vista["modo"])  # produccion

MappingProxyType no congela el diccionario; restringe la mutación a través de una referencia concreta.

Solo lectura no significa inmutabilidad profunda

La estructura externa no puede cambiarse mediante el proxy, pero los valores anidados conservan su mutabilidad.

datos = {
    "usuarios": ["Ana", "Bruno"],
    "opciones": {"debug": False},
}

vista = MappingProxyType(datos)
vista["usuarios"].append("Carla")
vista["opciones"]["debug"] = True

print(datos)

Para inmutabilidad profunda, normaliza los valores a tipos inmutables, realiza una copia profunda cuando corresponda o usa estructuras persistentes diseñadas para ese fin.

Exponer estado interno con seguridad

Una clase puede mantener un diccionario mutable internamente y exponer un proxy.

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, nombre: str, plugin: object) -> None:
        if nombre in self._plugins:
            raise ValueError(f"plugin duplicado: {nombre}")
        self._plugins[nombre] = plugin

Los consumidores pueden consultar plugins, pero deben usar registrar() para cambiar el estado. La clase conserva sus validaciones e invariantes.

Anotar lectores como Mapping

Las funciones que solo leen deberían aceptar collections.abc.Mapping en vez de dict.

from collections.abc import Mapping


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

Así se aceptan diccionarios, proxies y otros mappings, comunicando que la mutación no forma parte del contrato.

Configuración pública y privada

Un patrón común construye una configuración mutable durante el inicio y expone una vista de lectura.

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

CONFIG = MappingProxyType(_config)

El nombre en mayúsculas sugiere una constante y el proxy bloquea asignaciones accidentales. El código que conserva _config todavía puede modificar el origen.

Namespaces de clase

Python expone el namespace de una clase mediante un objeto parecido a mappingproxy en MiClase.__dict__. Esto evita ediciones directas que ignorarían mecanismos internos.

class Ejemplo:
    valor = 10

print(type(Ejemplo.__dict__))
print(Ejemplo.__dict__["valor"])
# Ejemplo.__dict__["valor"] = 20  # no permitido
Ejemplo.valor = 20

La asignación correcta permite que el runtime mantenga caches, descriptores e invariantes.

Comparación con dict.copy()

Una copia superficial crea un diccionario externo independiente.

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

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

Usa una copia cuando el consumidor necesite un snapshot independiente. Usa un proxy cuando deba observar cambios del propietario sin poder modificar la referencia expuesta.

Snapshots de solo lectura

Combina copia y proxy cuando quieras estabilidad y protección.

snapshot = MappingProxyType(dict(origen))

Los cambios posteriores en el origen no aparecen. Los objetos anidados siguen compartidos porque la copia es superficial.

Rendimiento y memoria

Crear un proxy es barato porque no duplica todas las entradas. Puede ser útil para registros grandes compartidos con muchos lectores. El acceso añade una pequeña indirección, normalmente irrelevante frente al beneficio de encapsulación.

No elijas MappingProxyType solamente por microoptimización. Su principal valor es expresar una interfaz de lectura y bloquear escrituras accidentales.

Concurrencia y thread safety

Un proxy no vuelve thread-safe al diccionario original. Si otra thread cambia el origen durante una iteración, los lectores pueden observar un estado intermedio o recibir “dictionary changed size during iteration”.

for clave, valor in proxy.items():
    ...  # evita cambios estructurales simultáneos

Usa locks, snapshots, sustitución atómica de referencias o estructuras específicas cuando necesites sincronización.

Serialización

Algunas bibliotecas no aceptan mappingproxy directamente. Convierte cuando una API exija un diccionario real.

import json

texto = json.dumps(dict(proxy))

La conversión crea una copia superficial y los valores anidados deben ser serializables.

Hash y uso como clave

Un proxy de solo lectura no debe considerarse automáticamente una clave profundamente inmutable. El origen puede cambiar. Para una representación estable, crea una estructura inmutable a partir de valores hashable.

clave = tuple(sorted(origen.items()))

Esto funciona solo si todos los valores son hashable y la normalización por orden es adecuada.

Errores comunes

  • Creer que los valores anidados quedan congelados: listas y diccionarios siguen mutables.
  • Esperar un snapshot: el proxy sigue los cambios del origen.
  • Usarlo como frontera de seguridad: quien posee la referencia original puede modificar.
  • Suponer thread safety: no existe sincronización automática.
  • Anotar lectores como dict: usa Mapping.
  • Convertir repetidamente a dict: se pierde la ventaja de evitar copias.

Ejemplo completo: catálogo versionado

from collections.abc import Mapping
from types import MappingProxyType

class Catalogo:
    def __init__(self) -> None:
        self._items: dict[str, float] = {}
        self._version = 0
        self._vista = MappingProxyType(self._items)

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

    @property
    def version(self) -> int:
        return self._version

    def definir_precio(self, codigo: str, precio: float) -> None:
        if precio < 0:
            raise ValueError("precio negativo")
        self._items[codigo] = precio
        self._version += 1

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

El catálogo controla las actualizaciones mientras los lectores observan el estado actual mediante una interfaz no mutable.

Cuándo usar otra solución

Usa frozenset para conjuntos inmutables, tuplas para secuencias fijas, dataclasses congeladas para registros con atributos y colecciones persistentes para actualizaciones funcionales eficientes. Una copia simple puede ser mejor si el mapping es pequeño y el aislamiento importa más que ver cambios en vivo.

Conclusión

types.MappingProxyType crea una vista dinámica de solo lectura sobre un mapping. Es útil para encapsular diccionarios internos, exponer registros y configuración, y comunicar permisos claros a los consumidores.

La documentación oficial de MappingProxyType en Python describe su comportamiento. Úsalo sabiendo que no ofrece inmutabilidad profunda, snapshot, seguridad ni sincronización; esas garantías requieren decisiones adicionales.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Retro fuel pump with rusty metal and vintage design, featuring a nozzle and liter meter.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Literal en Python: restringe valores

    Aprende typing.Literal en Python para restringir valores, crear overloads, discriminar TypedDict, usar match/case y mejorar APIs tipadas.

    Ler mais

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

    TypedDict en Python: diccionarios tipados

    Aprende TypedDict en Python para diccionarios tipados, claves opcionales, NotRequired, Required, payloads de APIs y variantes discriminadas.

    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 Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Protocol en Python: tipado estructural

    Aprende Python Protocol para tipado estructural, contratos genéricos, callbacks, runtime_checkable, pruebas e inyección de dependencias.

    Ler mais

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

    memoryview en Python: buffers sin copia

    Aprende memoryview en Python para buffers sin copia, slices, bytearray editable, cast, mmap, struct, sockets y control seguro del ciclo

    Ler mais

    Tempo de leitura: 4 minutos
    28/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    tracemalloc en Python: rastrea memoria

    Aprende tracemalloc en Python para medir picos, crear y comparar snapshots, filtrar asignaciones y diagnosticar crecimiento de memoria.

    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 Avanzado
    Foto de perfil de Leandro Hirt da Academify

    queue en Python: comunica threads

    Aprende queue en Python para comunicar threads con FIFO, LIFO, prioridad, backpressure, task tracking, sentinelas y shutdown seguro.

    Ler mais

    Tempo de leitura: 5 minutos
    28/08/2026