Как сделать словарь только для чтения в Python

У меня есть класс вроде:

class A:
    def __init__(self):
        self.data = {}

и в какой-то момент я хочу запретить модификацию полей self.data.

Я читал в уведомлении об отказе PEP-416, что есть много способов сделать это. Поэтому я хотел бы найти то, что они есть.

Я пробовал это:

a = A()
a.data = types.MappingProxyType(a.data)

Это должно работать, но сначала, его python3.3 +, а во-вторых, когда я делаю это "запрещение" несколько раз, я получаю следующее:

>>> a.data = types.MappingProxyType(a.data)
>>> a.data = types.MappingProxyType(a.data)
>>> a.data
mappingproxy(mappingproxy({}))

хотя было бы гораздо лучше получить только mappingproxy({}), поскольку я собираюсь "запрещать" много раз. Проверка isinstance(MappingProxyType) - это вариант, но я думаю, что могут существовать другие варианты.

Спасибо

Ответы

Ответ 1

Используйте collections.Mapping, например.

import collections

class DictWrapper(collections.Mapping):

    def __init__(self, data):
        self._data = data

    def __getitem__(self, key): 
        return self._data[key]

    def __len__(self):
        return len(self._data)

    def __iter__(self):
        return iter(self._data)

НТН,

Ответ 2

Это полная реализация read-only dict:

class ReadOnlyDict(dict):
    def __readonly__(self, *args, **kwargs):
        raise RuntimeError("Cannot modify ReadOnlyDict")
    __setitem__ = __readonly__
    __delitem__ = __readonly__
    pop = __readonly__
    popitem = __readonly__
    clear = __readonly__
    update = __readonly__
    setdefault = __readonly__
    del __readonly__

Ответ 3

Очень просто, вы просто переопределяете стандартные методы dict!
Вот пример:

class ReadOnlyDict(dict):

    __readonly = False

    def readonly(self, allow=1):
        """Allow or deny modifying dictionary"""
        self.__readonly = bool(allow)

    def __setitem__(self, key, value):

        if self.__readonly:
            raise TypeError, "__setitem__ is not supported"
        return dict.__setitem__(self, key, value)

    def __delitem__(self, key):

        if self.__readonly:
            raise TypeError, "__delitem__ is not supported"
        return dict.__delitem__(self, key)

Кстати, вы также можете удалить .pop, .update и другие методы, которые вам нужны. Просто поиграйте с ним.