Python:使用集合检测重复项

9 投票
5 回答
2676 浏览
提问于 2025-04-16 17:30

我有很多对象需要在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 个回答

2

哈希函数只是用来生成对象的唯一标识,但光有这个还不够,你还需要实现比较函数(也就是 __eq__)。

4

一个集合显然需要处理哈希冲突。如果两个对象的哈希值相同,集合会使用 == 操作符来比较它们,以确保它们真的相等。在你的情况下,只有当这两个对象是同一个对象时,这个比较才会返回 True(这是用户自定义类的标准实现)。

简单来说:还需要定义 __eq__() 方法才能让它正常工作。

13

你忘了还要 定义 __eq__()

如果一个类没有定义 __cmp__()__eq__() 方法,那么它也不应该定义 __hash__() 操作;如果它定义了 __cmp__()__eq__() 但没有定义 __hash__(),那么它的实例就不能在哈希集合中使用。如果一个类定义了可变对象,并且实现了 __cmp__()__eq__() 方法,那么它就不应该实现 __hash__(),因为哈希集合要求对象的哈希值是不可变的(如果对象的哈希值改变了,它就会被放到错误的哈希桶里)。

撰写回答