Python:使用集合检测重复项
我有很多对象需要在Python中存储以便处理。具体来说,我想从一大堆对象中去掉重复的。我希望如果对象中的某个实例变量相等,就认为这两个对象是“相等”的。因此,我想最简单的方法就是把所有对象放进一个集合里,并重写__hash__
方法,让它根据我关心的那个实例变量来计算哈希值。
所以,我做了一个测试,尝试了以下代码:
class Person:
def __init__(self, n, a):
self.name = n
self.age = a
def __hash__(self):
return hash(self.name)
def __str__(self):
return "{0}:{1}".format(self.name, self.age)
myset = set()
myset.add(Person("foo", 10))
myset.add(Person("bar", 20))
myset.add(Person("baz", 30))
myset.add(Person("foo", 1000)) # try adding a duplicate
for p in myset: print(p)
在这里,我定义了一个Person
类,任何两个Person
实例只要name
变量相同,就认为它们是相等的,不管其他实例变量的值是什么。不幸的是,程序输出了:
baz:30
foo:10
bar:20
foo:1000
注意到foo
出现了两次,所以这个程序没有发现重复的对象。然而,表达式hash(Person("foo", 10)) == hash(Person("foo", 1000))
的结果是True
。那么,为什么它没有正确检测到重复的Person
对象呢?
5 个回答
哈希函数只是用来生成对象的唯一标识,但光有这个还不够,你还需要实现比较函数(也就是 __eq__
)。
一个集合显然需要处理哈希冲突。如果两个对象的哈希值相同,集合会使用 ==
操作符来比较它们,以确保它们真的相等。在你的情况下,只有当这两个对象是同一个对象时,这个比较才会返回 True
(这是用户自定义类的标准实现)。
简单来说:还需要定义 __eq__()
方法才能让它正常工作。
你忘了还要 定义 __eq__()
。
如果一个类没有定义
__cmp__()
或__eq__()
方法,那么它也不应该定义__hash__()
操作;如果它定义了__cmp__()
或__eq__()
但没有定义__hash__()
,那么它的实例就不能在哈希集合中使用。如果一个类定义了可变对象,并且实现了__cmp__()
或__eq__()
方法,那么它就不应该实现__hash__()
,因为哈希集合要求对象的哈希值是不可变的(如果对象的哈希值改变了,它就会被放到错误的哈希桶里)。