How to create an immutable dictionary in python?
我想在python中使用subclassdict,这样子类的所有字典都是不可变的。
我不明白__hash__是如何影响不变的,因为在我的理解中,它只是表示物体的平等或不平等!
那么,是否可以使用__hash__来实现不变性?怎样?
更新:
目标是API的公共响应可以作为dict提供,必须作为全局变量共享。所以,不管怎样,它都需要完好无损?
- 这个问题以前被问过很多次(不幸的是,我现在没有一个链接)。对于大多数问题,试图制作一个不变的字典是错误的方法。你到底想达到什么目的?
- 这个链接怎么样:stackoverflow.com/q/2703599/623518
- 我将重写您的dict的__setitem__()方法。但是,请注意,这并不能保证dict的值是不可变的(例如,假设您的值是列表)。
- @乔尔科内特:你至少还需要覆盖__delitem__()、clear()、pop()、popitem()、setdefault()和update()。
- @斯文马纳克:哈哈哈,我忘了那些xd。是的,你会的。
- @有更简单的方法吗?为什么tuple是不变的?
- @Yugaljindle:是否有一个更简单的方法来实现你想要实现的目标取决于你想要实现的目标。很可能,是的!
我找到了一个官方的参考资料:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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/
- 最后,工作很棒,加上一个更自信的来源。
- 此PEP被拒绝。请阅读拒绝理由。
- 这个政治公众人物被拒绝了。
So, can __hash__ be used to implement immutability ?
不,它不能。对象可以变(或不变),不管它的__hash__方法做什么。
不可变对象与__hash__之间的关系是,由于不可变对象不能更改,__hash__返回的值在构造后保持不变。对于可变对象,可能是这样,也可能不是这样(建议的做法是这样的对象不能哈希)。
关于进一步的讨论,见第13707号问题:澄清hash()施工期。
- 好吧,默认情况下,用户定义的类既可以是可变的,也可以是散列的,但是散列无论如何都将保持不变!
- @斯文马纳赫:你说的是真的。我说的也是真的。我不知道你的推理方向…
- 我只是建议你回答的最后一句话有些不完整。不变性和哈希性之间还有更多的关系。
- 显然,我需要更具体一些。在Python中,可能随时间变化的哈希值是非常无用的。哈希的主要目的是可以在集合和字典中使用哈希对象。如果散列值改变,这将以奇怪和不可预测的方式失败。所以所有合理的散列实现都具有散列值保持不变的属性,没有异常。
- 你能提供一些示例代码How to write an immutable dict ?吗?
关于哈希性和可变性之间的关系:
为了有用,哈希实现需要满足以下属性:
使用==比较equal的两个对象的哈希值必须相等。
哈希值可能不会随时间变化。
这两个属性意味着在比较实例时,哈希类不能考虑可变属性,相反,在比较实例时,确实考虑可变属性的类是不可哈希的。不可变类可以成为可哈希类,而不需要进行任何比较。
所有内置可变类型都不可哈希,所有不可变内置类型都可哈希。这主要是上述观察的结果。
默认情况下,用户定义的类根据对象标识定义比较,并使用id()作为哈希。它们是可变的,但是在比较实例时不考虑可变的数据,因此可以将它们变为可哈希的。
使类可散列并不能以某种神奇的方式使其不可变。相反,要使字典在保持原始比较运算符的同时以合理的方式具有哈希性,首先需要使其不可变。
编辑:关于您的更新:
提供全局不可变字典等效项的方法有以下几种:
使用collections.namedtuple()实例代替。
使用具有只读属性的用户定义类。
我通常会这样做:
1 2 3 4
| _my_global_dict = {"a": 42,"b": 7}
def request_value(key):
return _my_global_dict[key] |
通过前面的下划线,您可以清楚地表明,_my_global_dict是一个实现细节,应用程序代码不需要涉及它。注意,如果字典值恰好是可变对象,则此代码仍允许修改字典值。如果需要,可以通过返回copy.copy()s或copy.deepcopy()s值来解决此问题。
- 那么,请给出一些代码,使之成为immutable?因为这只回答了一半的问题!
- @余加金德尔:正如我之前说过的很多次,使用不变的字典很可能是解决你的问题的错误方法,但是我们只能告诉你,如果你告诉我们你的问题实际上是什么。(而且,我觉得你对一个好的解决方案并不感兴趣,你也不太关注答案和评论。否则,您将已经找到一些示例代码,这些代码链接在上面的注释中。)
- @尤加金德尔:不,这肯定不是你此案唯一的解决办法。同样,代码是针对链接到问题的第二个注释中的不可变字典的。
- 我真的不理解您的代码是如何实现一个不可变的dict的。您为键查询编写了一个包装方法,但我想把它作为一个适当的dict对象提供,它可以执行除写操作以外的所有操作。我在问题中说了。另外,我不能用deepcopy,因为它很慢。
- @Yugaljindle:很抱歉不服从你的命令。我想我再也帮不了你了。祝你好运!
- 我将寻找解决方案。SO是帮助不订购的地方。感谢您的大力支持!