5

我有一组对象:

class Test(object):
    def __init__(self):
        self.i = random.randint(1,10)


res = set()

for i in range(0,1000):
    res.add(Test())

print len(res) = 1000

如何从一组对象中删除重复项?

感谢您的回答,它的工作:

class Test(object):
    def __init__(self, i):
        self.i = i
    #   self.i = random.randint(1,10)
    #   self.j = random.randint(1,20)

    def __keys(self):
        t = ()
        for key in self.__dict__:
            t = t + (self.__dict__[key],)
        return t

    def __eq__(self, other):
        return isinstance(other, Test) and self.__keys() == other.__keys()

    def __hash__(self):
        return hash(self.__keys())

res = set()

res.add(Test(2))
...
res.add(Test(8))

结果:[2,8,3,4,5,6,7]

但是如何保存订单?设置不支持顺序。例如,我可以使用列表代替设置吗?

4

4 回答 4

12

您的对象必须是可散列的(即必须具有__eq__()__hash__()定义)才能使集合与它们正常工作:

class Test(object):
    def __init__(self):
        self.i = random.randint(1, 10)

    def __eq__(self, other):
        return self.i == other.i

    def __hash__(self):
        return self.i

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

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

 

如果您有多个属性,请散列并比较其中的一个元组(谢谢,delnan):

class Test(object):
    def __init__(self):
        self.i = random.randint(1, 10)
        self.k = random.randint(1, 10)
        self.j = random.randint(1, 10)

    def __eq__(self, other):
        return (self.i, self.k, self.j) == (other.i, other.k, other.j)

    def __hash__(self):
        return hash((self.i, self.k, self.j))
于 2013-03-22T21:57:58.190 回答
1

Pavel Anossov 已经回答了您的第一个问题。

但是你还有一个问题:

但是如何保存订单?设置不支持顺序。例如,我可以使用列表代替设置吗?

可以使用 a list,但有一些缺点:

  • 你得到错误的界面。
  • 您不会自动处理重复项。你必须明确写if foo not in res: res.append(foo). 显然,您可以将其包装在一个函数中,而不是重复编写它,但这仍然是额外的工作。
  • 如果集合变得很大,效率会大大降低。基本上,添加一个新元素、检查一个元素是否已经存在等等都将是 O(N) 而不是 O(1)。

你想要的是像有序的东西一样工作的东西set。或者,等效地,就像list不允许重复的那样。

如果您先进行所有添加,然后进行所有查找,并且不需要快速查找,则可以通过首先构建 a 来解决此问题list,然后使用unique_everseenfrom the itertoolsrecipes删除重复项。

或者您可以按顺序保留 aset和 a listor 元素(或到目前为止看到的 a listplus a元素)。set但这可能会有点复杂,所以你可能想把它包起来。

理想情况下,您希望将其封装在与set. OrderedSet类似的东西collections.OrderedDict

幸运的是,如果您滚动到该文档页面的底部,您会看到您想要的内容已经存在;OrderedSetActiveState上有一个食谱的链接。

因此,复制它,将其粘贴到您的代码中,然后更改res = set()res = OrderedSet(),您就完成了。

于 2013-03-22T23:17:51.717 回答
0

我认为你可以很容易地按照你在第一篇文章中询问的那样使用列表来做你想做的事情,因为你定义了eq运算符:

l = []
if Test(0) not in l : 
    l.append(Test(0))

我的 2 克拉...

于 2013-03-22T22:57:50.403 回答
0

Pavel Anossov 的回答非常适合让您的类在具有您想要的语义的集合中使用。但是,如果您想保留项目的顺序,则需要更多。这是一个对列表进行重复数据删除的函数,只要列表项是可散列的:

def dedupe(lst):
    seen = set()
    results = []
    for item in lst:
        if item not in seen:
            seen.add(item)
            results.append(item)
    return results

一个稍微惯用的版本是生成器,而不是返回列表的函数。这摆脱了results变量,使用yield而不是附加唯一值。我还将lst参数重命名为iterable,因为它在任何可迭代对象(例如另一个生成器)上都可以正常工作。

def dedupe(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item
于 2013-03-22T23:08:00.093 回答