Immutable dictionary, only use as a key for another dictionary
我需要实现一个hashable dict,这样我就可以使用一个字典作为另一个字典的键。
几个月前我使用了这个实现:python hashable dicts
然而,我收到一位同事的通知,他说:"这并不是一成不变的,因此它是不安全的。"你可以用它,但它确实让我觉得自己像一只悲伤的熊猫。
所以我开始四处寻找,创造一个不变的。我不需要把"钥匙口述"和另一个"钥匙口述"进行比较。它的唯一用途是作为另一本字典的键。
我提出了以下几点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | class HashableDict(dict): """Hashable dict that can be used as a key in other dictionaries""" def __new__(self, *args, **kwargs): # create a new local dict, that will be used by the HashableDictBase closure class immutableDict = dict(*args, **kwargs) class HashableDictBase(object): """Hashable dict that can be used as a key in other dictionaries. This is now immutable""" def __key(self): """Return a tuple of the current keys""" return tuple((k, immutableDict[k]) for k in sorted(immutableDict)) def __hash__(self): """Return a hash of __key""" return hash(self.__key()) def __eq__(self, other): """Compare two __keys""" return self.__key() == other.__key() # pylint: disable-msg=W0212 def __repr__(self): """@see: dict.__repr__""" return immutableDict.__repr__() def __str__(self): """@see: dict.__str__""" return immutableDict.__str__() def __setattr__(self, *args): raise TypeError("can't modify immutable instance") __delattr__ = __setattr__ return HashableDictBase() |
我使用以下方法测试功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | d = {"a" : 1} a = HashableDict(d) b = HashableDict({"b" : 2}) print a d["b"] = 2 print a c = HashableDict({"a" : 1}) test = {a :"value with a dict as key (key a)", b :"value with a dict as key (key b)"} print test[a] print test[b] print test[c] |
号
它给出:
{'a': 1}
{'a': 1}
value with a dict as key (key a)
value with a dict as key (key b)
value with a dict as key (key a)
号
作为输出
这是"最好的"不变的字典,我可以使用它来满足我的要求吗?如果不是,那么什么是更好的解决方案呢?
如果您只将它用作另一个
1 2 3 4 | mutabledict = dict(zip('abc', range(3))) immutable = frozenset(mutabledict.items()) read_frozen = dict(immutable) read_frozen['a'] # => 1 |
注意,您也可以将它与派生自
映射抽象基类使得这很容易实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import collections class ImmutableDict(collections.Mapping): def __init__(self, somedict): self._dict = dict(somedict) # make a copy self._hash = None def __getitem__(self, key): return self._dict[key] def __len__(self): return len(self._dict) def __iter__(self): return iter(self._dict) def __hash__(self): if self._hash is None: self._hash = hash(frozenset(self._dict.items())) return self._hash def __eq__(self, other): return self._dict == other._dict |
。
为了使不可变字典安全,它所需要做的就是永远不要更改其哈希值。为什么不按如下方式禁用
1 2 3 4 5 6 7 8 9 10 11 | class ImmutableDict(dict): def __setitem__(self, key, value): raise Exception("Can't touch this") def __hash__(self): return hash(tuple(sorted(self.items()))) a = ImmutableDict({'a':1}) b = {a:1} print b print b[a] a['a'] = 0 |
号
脚本的输出为:
1 2 3 4 5 6 7 8 | {{'a': 1}: 1} 1 Traceback (most recent call last): File"ex.py", line 11, in <module> a['a'] = 0 File"ex.py", line 3, in __setitem__ raise Exception("Can't touch this") Exception: Can't touch this |
我知道这已经得到了回答,但是types.mappingProxyType是Python3.3的一个类似的实现。关于最初的安全问题,在PEP416中有一个讨论——添加一个frozendict内置类型,说明为什么拒绝
这里有一个指向
只需
更新:
看来我迟到了。不确定是否有人想出了主意。但这是我的看法。该dict是不可变的和可散列的。我用一个自定义的"readonly"函数重写了所有的方法(magic等),使其不可变,从而引发了异常。这是在对象实例化时完成的。为了解决无法应用值的问题,我在"新"下设置了"hash"。然后我重写"uuu hash"函数。就这样!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class ImmutableDict(dict): _HASH = None def __new__(cls, *args, **kwargs): ImmutableDict._HASH = hash(frozenset(args[0].items())) return super(ImmutableDict, cls).__new__(cls, args) def __hash__(self): return self._HASH def _readonly(self, *args, **kwards): raise TypeError("Cannot modify Immutable Instance") __delattr__ = __setattr__ = __setitem__ = pop = update = setdefault = clear = popitem = _readonly |
测试:
immutabled1 = ImmutableDict({"This":"That","Cheese":"Blarg"})
dict1 = {immutabled1:"Yay"}
dict1[immutabled1]
"Yay"
dict1
{{'Cheese': 'Blarg', 'This': 'That'}: 'Yay'}
号
用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class ImmutableDict(collections.Mapping): """ Copies a dict and proxies it via types.MappingProxyType to make it immutable. """ def __init__(self, somedict): dictcopy = dict(somedict) # make a copy self._dict = MappingProxyType(dictcopy) # lock it self._hash = None def __getitem__(self, key): return self._dict[key] def __len__(self): return len(self._dict) def __iter__(self): return iter(self._dict) def __hash__(self): if self._hash is None: self._hash = hash(frozenset(self._dict.items())) return self._hash def __eq__(self, other): return self._dict == other._dict def __repr__(self): return str(self._dict) |
。