7

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

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

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

更新

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

4

4 回答 4

9

我找到了官方参考:被拒绝的 PEP 中包含的建议。

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

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

于 2012-06-14T04:42:00.510 回答
6

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

不,它不能。无论其__hash__方法做什么,都可以使对象可变(或不可变)。

不可变对象 和 之间的关系__hash__是,由于不可变对象无法更改,因此返回的值在__hash__构造后保持不变。对于可变对象,这可能是也可能不是(推荐的做法是这些对象根本无法散列)。

如需进一步讨论,请参阅问题 13707:澄清hash()选区

于 2012-06-13T11:55:29.040 回答
5

关于哈希性和可变性之间的关系:

为了有用,哈希实现需要满足以下属性:

  1. 比较相等的两个对象的哈希值==必须相等。

  2. 哈希值可能不会随时间变化。

这两个属性意味着可散列的类在比较实例时不能考虑可变属性,并且相反,在比较实例时确实考虑了可变属性的类是不可散列的。不可变类可以在不影响比较的情况下进行哈希处理。

所有内置的可变类型都不是可散列的,所有不可变的内置类型都是可散列的。这主要是上述观察的结果。

默认情况下,用户定义的类定义基于对象标识的比较,并使用id()as hash。它们是可变的,但在比较实例时不考虑可变数据,因此可以将它们设为可散列的。

使一个类可散列并不会以某种神奇的方式使其不可变。相反,要在保持原始比较运算符的同时以合理的方式使字典可散列,您首先需要使其不可变。

编辑:关于您的更新:

有几种方法可以提供等效的全局不可变字典:

  1. 请改用collections.namedtuple()实例。

  2. 使用具有只读属性的用户定义类。

  3. 我通常会这样做:

    _my_global_dict = {"a": 42, "b": 7}
    
    def request_value(key):
        return _my_global_dict[key]
    

    通过前导下划线,您可以清楚地表明这_my_global_dict是应用程序代码不涉及的实现细节。请注意,如果字典值恰好是可变对象,则此代码仍允许修改它们。如有必要,您可以通过返回copy.copy()s 或s 个值来解决此问题。copy.deepcopy()

于 2012-06-13T12:34:43.563 回答
1

frozendict中,hash 在被拒绝的PEP 416之后简单地实现:

def __hash__(self):
    try:
        fs = frozenset(self.items())
    except TypeError:
        hash = -1
    else:
        hash = hash(fs)
    
    if hash == -1:
        raise TypeError("Not all values are hashable.")
    
    return hash

PS:我是包的新维护者。

于 2022-01-15T21:09:33.300 回答