【问题标题】:Mutable Wrapper for Immutable Dictionary不可变字典的可变包装器
【发布时间】:2019-04-26 22:05:22
【问题描述】:

这就是问题所在;我有一个包含大量项目的不可变字典。此 dict 中包含的键类型和值类型本身是不可变的。我希望能够改变这个字典(添加/删除/替换键值对),而不必制作字典的完整副本。

我正在想象一些不可变字典的包装类,它遵守字典契约,并且默认为未更新的值的不可变字典。我看到了How to “perfectly” override a dict? 的帖子,我打算利用它来制作这个包装器。

在我着手实现这个设计之前,我只想问一下——这个语言是否已经提供了这个结构?或者我怎样才能达到预期的效果?我使用的是最新版本的 Python (3.7),因此我可以使用所有可用的语言功能。谢谢!

【问题讨论】:

    标签: python dictionary memory immutability python-3.7


    【解决方案1】:

    看看collections.ChainMap。它是多个字典的包装器:所有写入都转到第一个字典,并按照映射的顺序搜索查找。所以我认为你可以这样做:

    modified_map = {}
    mutable_map = collections.ChainMap(modified_map, huge_immutable_map)
    

    【讨论】:

    • Looks like it does! "查找连续搜索底层映射,直到找到一个键。相反,写入、更新和删除只对第一个映射进行操作。"太棒了,非常感谢!
    • 我想它不能做的一件事是删除不可变字典中的项目,如果你想这样做的话。
    • 不,我希望不可变 dict 保持不可变。我只是想阻止完整的副本能够这样做。
    【解决方案2】:

    假设您使用了frozendict 实现,例如this one

    class frozendict(collections.Mapping):
        """
        An immutable wrapper around dictionaries that implements the complete :py:class:`collections.Mapping`
        interface. It can be used as a drop-in replacement for dictionaries where immutability is desired.
        """
    
        dict_cls = dict
    
        def __init__(self, *args, **kwargs):
            self._dict = self.dict_cls(*args, **kwargs)
            self._hash = None
    
        def __getitem__(self, key):
            return self._dict[key]
    
        def __contains__(self, key):
            return key in self._dict
    
        def copy(self, **add_or_replace):
            return self.__class__(self, **add_or_replace)
    
        def __iter__(self):
            return iter(self._dict)
    
        def __len__(self):
            return len(self._dict)
    
        def __repr__(self):
            return '<%s %r>' % (self.__class__.__name__, self._dict)
    
        def __hash__(self):
            if self._hash is None:
                h = 0
                for key, value in self._dict.items():
                    h ^= hash((key, value))
                self._hash = h
            return self._hash
    

    如果你想改变它,你可以直接进入并改变self._dict

    d = frozendict({'a': 1, 'b': 2})
    d['a'] = 3  # This fails
    mutable_dict = d._dict
    mutable_dict['a'] = 3  # This works
    print(d['a'])
    

    接触一个类的受保护成员有点恶心,但我想说这没关系,因为你想做的事情有点恶心。如果你想要一个可变字典(只是一个dict),那么使用一个。如果您不想改变它,请使用上面的frozendict 实现。可变和不可变的混合是没有意义的。 frozendict 所做的只是它没有实现突变 dunder 方法(__setitem____delitem__ 等)。在后台,frozendict 由一个常规的、可变的 dict 表示。

    在我看来,上述方法优于您链接的方法。在许多情况下,可组合性(frozendict 具有 _dict 属性)比继承(子类化 dict)更容易推理。

    【讨论】:

    • 啊,我不确定我在原始问题中是否清楚。我不想改变不可变的支持字典。它与其他代码共享。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 2013-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-24
    相关资源
    最近更新 更多