在Python字典中使用对象作为键

33 投票
2 回答
52255 浏览
提问于 2025-04-16 11:32

我正在尝试在Python字典中使用一个对象作为键,但它的表现让我有点困惑。

首先,我创建了一个字典,使用我的对象作为键:

package_disseminators = {
  ContentType("application", "zip", "http://other/property") : "one",
  ContentType("application", "zip") : "two"
}

接下来,我创建了另一个与作为键的对象“相同”的对象。

content_type = ContentType("application", "zip", "http://other/property")

我给ContentType对象定义了自定义的__eq____str__方法,这样__eq__方法就可以比较__str__的值。

现在,进行一些交互式的Python操作:

>>> for key in package_disseminators:
...     if key == content_type:
...             print "match"
...     else:
...             print "no match"
... 
no match
match

>>> content_type in package_disseminators.keys()
True

好的,看起来我的对象确实被正确识别为一个键,所以:

>>> package_disseminators[content_type]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: (& (type="application/zip") (packaging="http://other/property") )

呃……好吧?所以content_type在package_disseminators.keys()列表中,但却不是一个键?

>>> package_disseminators.has_key(content_type)
False

显然不是。

我猜Python用来判断相等性的比较过程,在直接使用“in”语句检查列表和在字典中查找键时是不同的,但我不知道具体是怎样的。有什么建议或见解吗?

2 个回答

36

因为字典在内部是哈希表,所以你需要同时定义 __eq____hash__ 才能让它正常工作。

基本的原则是:

  • 对于那些 __eq__ 比较结果相等的对象,__hash__ 必须返回相同的哈希值。

根据你的描述,像下面这样的代码:

def __hash__(self):
    return hash(str(self))

应该可以正常工作。

46

来自Python文档的内容:

字典的键几乎可以是任意值。那些不可哈希的值,比如包含列表、字典或其他可变类型的值(这些值是通过内容比较,而不是通过对象身份比较)不能用作键。

哈希值的定义如下:

一个对象是可哈希的,如果它有一个在其生命周期内永远不变的哈希值(这需要一个 __hash__() 方法),并且可以与其他对象进行比较(这需要一个 __eq__()__cmp__() 方法)。可哈希的对象如果相等,必须有相同的哈希值。

可哈希性使得一个对象可以用作字典的键和集合的成员,因为这些数据结构在内部使用哈希值。

所以如果你想这样做,你需要重写你对象的默认 __hash__() 方法(可以参考下面Steven Rumbalski的评论以获取更多解释)。


>>> content_type in package_disseminators.keys()
True

我想这之所以有效,是因为 dict.keys() 返回的是一个列表,而 __contains__ 可能是检查相等性,而不是检查哈希值是否相同。

撰写回答