如何删除对象集中的重复项?

时间:2013-03-22 21:55:42

标签: python

我有一组对象:

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 个答案:

答案 0 :(得分:11)

您的对象必须 hashable (即必须定义__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__()或{{ 1}}方法)。比较相等的Hashable对象必须具有相同的哈希值。

     

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

如果你有几个属性,哈希并比较它们的元组(谢谢,__cmp__()):

delnan

答案 1 :(得分:1)

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

但你有另一个问题:

  

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

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

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

您想要的是有序的set。或者,等同于list,不允许重复。

如果您先完成所有添加操作,然后执行所有查找操作,并且不需要快速查找,则可以首先构建list,然后使用unique_everseen来解决此问题。从itertools recipes删除重复项。

或者您可以按顺序保留setlist或元素(或者到目前为止看到的list加上set个元素。但这可能会有点复杂,所以你可能想把它包起来。

理想情况下,您希望将其包装在与set具有完全相同API的类型中。像OrderedSet类似collections.OrderedDict的东西。

幸运的是,如果你滚动到该文档页面的底部,你会看到你想要的确切存在;在ActiveState上有一个OrderedSet食谱的链接。

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

答案 2 :(得分:0)

我认为,由于您定义了 eq 运算符,因此您可以轻松按照第一篇文章中的要求使用列表执行所需操作:

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

我的2 cts ......

答案 3 :(得分: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