【问题标题】:How to make dictionary read-only in Python如何在 Python 中将字典设为只读
【发布时间】:2013-10-02 02:30:35
【问题描述】:

我有这样的课程:

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

有时我想禁止self.data字段修改。

我在PEP-416 rejection notice 中读到有很多方法可以做到这一点。所以我想知道它们是什么。

我试过这个:

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) 是一个选项,但我认为可以存在其他选项。

谢谢

【问题讨论】:

标签: python dictionary


【解决方案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】:

这是快速(浅)只读字典的完整实现:​​

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__

【讨论】:

  • 要允许使用copy.copycopy.deepcopy 复制它们,您可能需要添加以下两行:__copy__ = dict.copy__deepcopy__ = copy._deepcopy_dispatch.get(dict)。虽然没有经过太多测试。
  • 对,但是如何初始化这个或者如何让现有的dict使用这个?
  • ro_dict = ReadOnlyDict(rw_dict)
【解决方案3】:

很简单,你只需覆盖默认字典的方法!
这是一个例子:

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 和其他您需要的方法。随便玩玩吧。

【讨论】:

  • 你如何“在某个时刻”应用这个?我猜 OP 需要将 dict 进行读写,然后在一段时间后将其冻结。
  • 如果他需要,他也可以修改它们
  • 这种方法很明显,但并不简单,因为我必须重写几乎每个 dict 方法,甚至重新实现 dict 视图,如 .keys().items() 等。如果 Guido 添加一些会怎样在未来的 python 版本中更多?无论如何,谢谢你的观点,但我希望有更多的答案。
【解决方案4】:

最好的方法是像这样从UserDict 派生:

from collections import UserDict

class MyReadOnlyDict(UserDict):
   def my_set(self, key, val, more_params):
       # do something special
       # custom logic etc
       self.data[key] = val

   def __setitem__(self, key, val):
       raise NotImplementedError('This dictionary cannot be updated')

   def __delitem__(self, key):
       raise NotImplementedError('This dictionary does not allow delete')

这种方法的优点是你的类中仍然可以有内部方法,可以通过访问self.data来更新字典。

【讨论】:

    【解决方案5】:

    怎么样?这是@mouad 答案的更新。

    import json
    from collections import OrderedDict
    from collections.abc import Mapping
    
    
    class ReadOnlyJsonObject(Mapping):
        def __init__(self, data, dumps_kw: dict=None, loads_kw: dict=None):
            if dumps_kw is None:
                dumps_kw = dict()
            if loads_kw is None:
                self._loads_kw = dict(object_pairs_hook=OrderedDict)
            else:
                self._loads_kw = loads_kw
    
            if isinstance(data, str):
                self._json_string = data
            else:
                self._json_string = json.dumps(data, **dumps_kw)
    
        @property
        def _data(self):
            return json.loads(self._json_string, **self._loads_kw)
    
        def __getitem__(self, key):
            return self._data[key]
    
        def __len__(self):
            return len(self._data)
    
        def __iter__(self):
            return iter(self._data)
    
        def __str__(self):
            return self._json_string
    

    但不确定性能。我在实际项目中使用了这个https://github.com/patarapolw/AnkiTools/blob/master/AnkiTools/tools/defaults.py

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-07
      • 1970-01-01
      • 1970-01-01
      • 2016-02-24
      • 1970-01-01
      • 1970-01-01
      • 2017-03-02
      相关资源
      最近更新 更多