如何使我的类实例可用作字典键?

26
class A():
   def __init__(self, data=''):
       self.data = data  

   def __str__(self):
       return str(self.data)

d = {}  
elem = A()  
d[elem] = 'abc'  

elem2 = A()
print d[elem2]    # KeyError  
# actually elem2! was used not elem

如何在没有错误的情况下实现这个功能?
我尝试使用另一个A()的实例,但内容相同,来获取d[elem2](而不是elem)。

什么错误(信息)?它是否只在访问元素时发生,而不是设置元素时发生?您是否真的(如示例中所示)在两行中使用相同的实例,还是它等同于 d[A()] = ...; print d[A()] - user395760
是的,只有在获取时才会出现,而不是设置。编辑:关键错误。 - Sergey
1
@Sergey:您需要告诉我们更多关于类A的信息。您是否覆盖了任何特殊方法?在发布错误消息时,请提供完整的错误消息,包括回溯信息。 - Sven Marnach
将上述代码中的 defined initstr 定义如我所写。 - Sergey
更新后的代码在我的电脑上运行没有错误。 - Sven Marnach
抱歉大家,我修改了问题。 - Sergey
2个回答

30
答案是肯定的,您需要重新定义__hash__()__eq__():
>>> class A(object):
...   def __init__(self, data=''):
...     self.data = data
...   def __eq__(self, another):
...     return hasattr(another, 'data') and self.data == another.data
...   def __hash__(self):
...     return hash(self.data)
... 
>>> a1, a2, a3 = A('foo'), A('foo'), A('bar')
>>> d = {a1: 'foo'}
>>> d[a1]
'foo'
>>> d[a2]
'foo'
>>> d[a3]
Traceback (most recent call last):
  File "", line 1, in 
KeyError: __main__.A object at 0x927d0>

如另一条评论所解释的那样,__hash__ 的默认实现只是简单的标识,因此如果您想使其更加复杂,您需要显式地定义它。


5
你还需要实现 __eq__ 方法,否则会发生奇怪的事情(至少在 Python 2.7 中是这样),我已经为你测试过了。 - Joël

6

只要您不覆盖 __hash__() __eq__() 方法,您所做的应该可以工作。它将使用对象标识作为相等性。如果您想要不同的相等性概念,您可以覆盖您的类的 __hash__() __eq__() 方法。


一个小的__hash__和__eq__的例子会很好。 - Sergey
什么定义了对象的身份?也就是说,如果更改哪些字段,该对象会表示另一个实体?假设该对象代表一个人并包含唯一标识符(例如社会安全号码),那么您可能会决定,如果两个对象的 ssn 属性相同,则它们是相等的,即使它们的名称不同(因为人们可以更改他们的名字)。然后您将编写 __eq__() 来仅比较 self.ssn 并编写 __hash__()以返回 hash(self.ssn)。根据对象表示的内容,可能会考虑任意数量的字段。 - kindall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接