首页 > 解决方案 > 具有身份相等性的 Dict 或 WeakKeyDictionary -- 包装不可散列的对象以检查身份

问题描述

我想使用一些对象作为某些 dict 的键,这些对象要么是不可散列的,要么是可散列的,但我想用默认的/ 覆盖它们的 / __eq__,即iff 。__hash__object.__eq__object.__hash__a == ba is b

(这些对象可能是numpy.ndarray,torch.Tensor或其他东西,但我现在想问一下。)

例如:

x = numpy.array([2,3,4])
d = {x: 5}

那将给出例外TypeError: unhashable type: 'numpy.ndarray'

或者:

x = torch.Tensor([2,3,4])
d = weakref.WeakKeyDictionary()
d[x] = 5
print(d[x])

那将给出例外RuntimeError: Boolean value of Tensor with more than one value is ambiguous。(这是相当误导或出乎意料的。但我认为这是因为它会在bool(__eq__(...))内部执行。但是,奇怪的是,当我在这里使用法线时没有这样的例外dict。为什么?)

我可以编写一个自定义对象包装器来解决这个问题,例如:

class WrappedObject:
  def __init__(self, orig):
    self.orig = orig
  
  def __eq__(self, other):
    return object.__eq__(self.orig, other.orig)

  def __ne__(self, other):
    return object.__ne__(self.orig, other.orig)

  def __hash__(self):
    return object.__hash__(self.orig)

这将解决第一种情况。现在我可以写:

x = numpy.array([2,3,4])
d = {WrappedObject(x): 5}
print(d[WrappedObject(x)])  # 5

WrappedObject在某些标准库中是否有类似的东西?

id函数具有类似的行为,尽管它只返回一个int,并且没有对原始对象的引用。所以对于这个例子,我可以写:

x = numpy.array([2,3,4])
d = {id(x): 5}
print(d[id(x)])  # 5

请注意,这可能有问题!如果x稍后被释放,那么理论上可以创建另一个具有相同的对象id,因为id只保证在生命周期内是唯一的,而不是在其生命周期之后。(这里的相关问题,虽然接受的答案确实有这个问题。)

不会发生此问题WrappedObject,因为引用始终保持活动状态。

是否有某种东西会自动包装以像在引擎盖下dict一样使用某物?WrappedObject即特别是我想要所有的键,它只使用它们的身份来实现平等。

现在考虑我的第二种情况,特别是WeakKeyDictionary. 我不能使用WrappedObject,因为它WrappedObject本身没有保持活动状态,所以所有键都会立即消失:

x = torch.Tensor([2,3,4])
d = weakref.WeakKeyDictionary()
d[WrappedObject(x)] = 5
print(list(d.items()))  # prints []

我目前看到的唯一真正的解决方案是重新实现WeakKeyDictionary自己,使用 sth like WrappedRefObject. 有更好的解决方案吗?这是否已经存在于标准库或其他地方?

标签: pythonweak-references

解决方案


推荐阅读