Python:自定义可变 class 可以用作字典的键吗?

Python: Can a custom mutable class be used as a key for a dictionary?

假设我们有一个像这样的自定义节点 class:

class Node:
    def __init__(self, val, next, random):
        self.val = val
        self.next = next
        self.random = random

我有一个节点对象,我想将其用作字典的键。
我的理解是,一个对象应该是不可变的和可哈希的,才能可靠地用作字典键,因为可变对象可能会导致哈希值发生变化,而这个对象是可变的。

我知道 python 允许将自定义可变对象用作字典键,这是如何工作的?

更新:引用的链接不涉及自定义对象的可变性方面。它们只是提供了一种方法来覆盖散列函数的默认实现。这个问题应该重新打开,因为它与引用的 "duplicate" 个问题不同。

答案:自定义可变对象的散列方法的默认实现使用identity,它保证在对象的生命周期内是唯一且不变的。一个可变的自定义对象不应该覆盖哈希函数的默认实现。下面提供了更详细的答案。

节点自定义对象根据其 class 定义是可变的,任何可变对象不应覆盖散列方法的默认实现,因为默认方法使用 identity ,保证在对象的生命周期内是唯一且不变的。这样做 可能会 导致对象的哈希值发生变化,这会导致很多问题。

例如:

Node node = new Node(5, None, None)
cache = {}
cache[node] = "Node added"
node.val = 5

# If we override default implementation of hash function, we have to
# make sure hashing function does not use object attributes else
# accessing cache[node] now would cause problem as a different hashed 
# value would be computed and we won't be able to retrieve "Node added" 
# value. 

# Default implementation uses identity and would always hash 
# to same value even if the object mutates.

但是我们可以继续为不可变对象覆盖散列方法。 this 线程上的回答帮助我理解了它是如何工作的。对于以下内容,我们将假设 Node 对象的属性一旦创建就永远不会改变。

这里的想法是确保您的自定义对象实现散列和相等方法。

相等方法的实现应使两个逻辑等价对象始终被视为相等。对于此处给定的 Node 对象,如果两个节点的所有属性都相同,则认为两个节点相等。所以我们可以构建一个键,它是所有属性的元组。这是可行的,因为元组是不可变的,所以我们基本上是在创建一个包含对象所有属性的不可变对象,并将使用它来计算哈希值。

以下实现确保两个逻辑上等效的节点(具有相同的属性)具有相同的键,因此将为两个相等的节点生成相同的哈希值。

class Node:
    def __init__(self, val, next, random):
        self.val = val
        self.next = next
        self.random = random

    def __key(self):
        return (self.val, self.next, self.random)

    def __hash__(self):
        return hash(self.__key())

    def __eq__(self, other):
        if isinstance(other, Node):
            return self.__key() == other.__key()
        return NotImplemented