我需要一个类似字典的结构,该结构可以采用无法散列的键并将它们映射为一个值。我有两个原因需要它:
遍历列表时检查在 O(1)中是否已经看到某项
将每个项目映射到一个标识符,例如一个字符
创建的类似dict的结构将在此过程后被丢弃,因此一旦对键进行突变就无法使用。
In [8]: np.arange(1,10)
Out[8]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
In [9]: np.arange(3*3)
Out[9]: array([0, 1, 2, 3, 4, 5, 6, 7, 8])
tl; dr我实现了一些行不通的方法。如果您看到实现此目标的规范方法,请跳过该部分。
现在,我编写了一个包装器类,该包装器实现了一个d = MutableKeyDict()
d[[1, 2, 3]] = 'a'
print([1, 2, 3] in d) # True
print((1, 2, 3) in d) # False
方法,该方法依赖于等同于对其数据进行哈希处理的不可变类型。
__hash__
这使我能够编写一个自定义dict类的草稿,该类使用class ForcedHashable:
@staticmethod
def hashable(obj):
try:
hash(obj)
return obj
except TypeError:
if isinstance(obj, (list, tuple)):
return tuple(ForcedHashable.hashable(o) for o in obj)
elif isinstance(obj, set):
return frozenset(ForcedHashable(o) for o in obj)
elif isinstance(obj, dict):
return tuple((k, ForcedHashable.hashable(v)) for k, v in obj.items())
...
def __init__(self, data):
self.data = data
def __eq__(self, other):
return self.data == other.data
def __hash__(self):
return hash(self.hashable(self.data))
来包装其键。
ForcedHashable
它适用于基本情况...
class MutableKeyDict(UserDict):
def __setitem__(self, key, value):
self.data[ForcedHashable(key)] = value
def __getitem__(self, item):
return self.data[ForcedHashable(item)]
def __contains__(self, item):
return ForcedHashable(item) in self.data
但是遇到嵌套对象的问题。
d = MutableKeyDict()
d[[1, 2, 3]] = 'a'
print([1, 2, 3] in d) # True
print((1, 2, 3) in d) # False
当然,递归源自该语句:
d = MutableKeyDict()
x = []
x.append(x)
d[x] = 'foo' # raises a 'RecursionError: maximum recursion depth exceeded'
我刚刚完成了if isinstance(obj, (list, tuple)):
return tuple(ForcedHashable.hashable(o) for o in obj)
的修复工作,有点像memo
所使用的修复程序,但是后来我意识到,即使我这样做了,该方法也会引发{{ 1}}。
copy.deepcopy
我希望以上内容至少适用于内置类型。
会不会有一种聪明的方法来解决RecursionError
?如果没有,是否存在将相等项(仅内置类型)关联到临时哈希的规范方法?我们欢迎其他方法。
答案 0 :(得分:2)
没有理由使用deepcopy
技术来解决递归问题。
我认为您可能会遗漏的是deepcopy
的记忆是基于值id
的。同样,您只需要捕获包含自己的对象,而不是包含相等但不同对象的对象。毕竟,您不可能拥有无穷但相等的物体的无限深度。这将需要无限的内存。
实际上,您可以使它比deepcopy
和pickle
更简单,因为对于重复的对象,返回的 并不重要,只要它是可散列且唯一。 1
例如,
def hashable(obj, *, memo=None):
if memo is None:
memo = set()
if id(obj) in memo:
return (..., id(obj))
memo.add(id(obj))
try:
hash(obj)
return obj
except TypeError:
if isinstance(obj, (list, tuple)):
return tuple(ForcedHashable.hashable(o, memo=memo) for o in obj)
elif isinstance(obj, set):
return frozenset(ForcedHashable(o, memo=memo) for o in obj)
elif isinstance(obj, dict):
return frozenset((k, ForcedHashable.hashable(v, memo=memo)) for k, v in obj.items())
raise
现在:
>>> x = []
>>> x.append(x)
>>> ForcedHashable.hashable(x)
((Ellipsis, 4658316360),)
>>> d = MutableKeyDict()
>>> d[x] = d
>>> d[x]
{<__main__.ForcedHashable object at 0x115855240>: 2, <__main__.ForcedHashable object at 0x115a247f0>: {...}}
在我们这样做的时候,请执行以下操作:
elif isinstance(obj, (dict, MutableKeyDict)):
return frozenset((k, ForcedHashable.hashable(v, memo=memo)) for k, v in obj.items())
…现在:
>>> d = MutableKeyDict()
>>> d[d] = d
>>> d
{<__main__.ForcedHashable object at 0x11584b320>: {...}}
1。除非您希望它们像Quine原子一样工作,否则您希望它可以被散列并由相同类型的所有其他Quine原子共享,这同样容易。