在Python字典中使用对象作为键
我正在尝试在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 个回答
因为字典在内部是哈希表,所以你需要同时定义 __eq__
和 __hash__
才能让它正常工作。
基本的原则是:
- 对于那些
__eq__
比较结果相等的对象,__hash__
必须返回相同的哈希值。
根据你的描述,像下面这样的代码:
def __hash__(self):
return hash(str(self))
应该可以正常工作。
来自Python文档的内容:
字典的键几乎可以是任意值。那些不可哈希的值,比如包含列表、字典或其他可变类型的值(这些值是通过内容比较,而不是通过对象身份比较)不能用作键。
哈希值的定义如下:
一个对象是可哈希的,如果它有一个在其生命周期内永远不变的哈希值(这需要一个
__hash__()
方法),并且可以与其他对象进行比较(这需要一个__eq__()
或__cmp__()
方法)。可哈希的对象如果相等,必须有相同的哈希值。可哈希性使得一个对象可以用作字典的键和集合的成员,因为这些数据结构在内部使用哈希值。
所以如果你想这样做,你需要重写你对象的默认 __hash__()
方法(可以参考下面Steven Rumbalski的评论以获取更多解释)。
>>> content_type in package_disseminators.keys()
True
我想这之所以有效,是因为 dict.keys()
返回的是一个列表,而 __contains__
可能是检查相等性,而不是检查哈希值是否相同。