如何在python中创建不可变的字典?

Yug*_*dle 4 python class subclass immutability

我想subclass dict在python中使子类的所有字典都是不可变的.

我不明白如何__hash__影响不变性,因为在我的理解中它只是表示对象的相等不相等!

那么,可以__hash__用来实现不变性吗?怎么样 ?

更新:

目标是来自API的常见响应可用作dict,必须作为全局变量共享.那么,无论如何都需要完好无损?

Kon*_*nin 29

可以immutable dict仅使用标准库来创建。

from types import MappingProxyType

power_levels = MappingProxyType(
    {
        "Kevin": 9001,
        "Benny": 8000,
    }
)
Run Code Online (Sandbox Code Playgroud)

查看想法来源和更详细的解释


Mar*_*lla 16

freezedict中,哈希是按照 Victor Stinner 拒绝的PEP 416简单实现的:

def __hash__(self):
    try:
        fs = frozenset(self.items())
    except TypeError:
        hash = -1
    else:
        hash = hash(fs)
    
    if hash == -1:
        raise TypeError(f"not all values are hashable: '{self.__class__.__name__}'")
    
    return hash
Run Code Online (Sandbox Code Playgroud)

关于哈希性和平等性,它们是不一样的。
即使两个对象是可变的,它们也可以是相等的。
例如,{1:2, 3:4} == {3:4, 1:2}
通常,可哈希对象是不可变的,但这不是必需的。
此外,通常具有相同哈希值的对象也是相等的,但这并不总是正确的。
例如,hash(-1) == hash(-2) == -2
这称为哈希冲突。

进一步阅读:
https://docs.python.org/3/glossary.html#term-hashable
https://en.wikipedia.org/wiki/Hash_collision

PS:我是该软件包的新维护者frozendict

  • 截至 2023 年,这是我认为最好的答案!基于 PEP 416 中已存在的内容构建,与 stdlib 中的 MappingProxyType 对象不同,它可以进行 pickle。在元层面上,它直接解决了答案! (2认同)

Yug*_*dle 9

我找到了一个官方参考:

class imdict(dict):
    def __hash__(self):
        return id(self)

    def _immutable(self, *args, **kws):
        raise TypeError('object is immutable')

    __setitem__ = _immutable
    __delitem__ = _immutable
    clear       = _immutable
    update      = _immutable
    setdefault  = _immutable
    pop         = _immutable
    popitem     = _immutable
Run Code Online (Sandbox Code Playgroud)

归因:http://www.python.org/dev/peps/pep-0351/

  • 这是一个糟糕的答案,绝对不是官方的。您不应该从 dict 派生,也没有必要重写这么多不必要的方法。 (6认同)
  • 顺便说一下,此 PEP 被拒绝了 (4认同)
  • 这项PEP被拒绝。请阅读拒绝理由。 (2认同)
  • 编辑以删除“官方参考”的声明 (2认同)

Leo*_*ael 7

从 Python 3.3 开始,可以用来MappingProxyType创建不可变映射:

>>> from types import MappingProxyType
>>> MappingProxyType({'a': 1})
mappingproxy({'a': 1})
>>> immutable_mapping = MappingProxyType({'a': 1})
>>> immutable_mapping['a']
1
>>> immutable_mapping['b'] = 2
Traceback (most recent call last):
  (...)
TypeError: 'mappingproxy' object does not support item assignment
Run Code Online (Sandbox Code Playgroud)

它不可散列,因此您不能将其用作字典键(并且它是“最终”键,因此您不能将其子类化以覆盖__hash__),但如果您想要一个不可变的映射来防止意外修改全局值,那么它就足够了(就像类的默认属性)。

小心不要添加本身可以修改的可变值。


NPE*_*NPE 5

那么,可以__hash__用来实现不变性吗?

不,它不能.无论其__hash__方法如何,对象都可以变得可变(或不变).

不可变对象之间的关系__hash__是,由于无法更改不可变对象,因此返回的值在__hash__构造后保持不变.对于可变对象,这可能是也可能不是这种情况(推荐的做法是这样的对象根本无法哈希).

有关进一步的讨论,请参阅问题13707:澄清合hash()规期.