2013-03-22 73 views
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]

但是如何保存順序?設置不支持順序。例如,我可以使用列表而不是集?

回答

9

你的對象必須是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__()__cmp__()方法)。比較相等的哈希對象必須具有相同的哈希值。

Hashability使對象可用作字典鍵和集合成員,因爲這些數據結構在內部使用散列值。

 

如果你有幾個屬性,哈希和比較它們的元組(感謝,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)) 
+0

謝謝,但如果我有幾個attrs? – Bdfy 2013-03-22 22:06:23

+0

將它們混合以產生唯一的整數散列(例如,在非整數屬性上使用buitlin'hash'函數,並將它們一起使用),並以對您有意義的方式定義相等性。這兩個對象必須共同考慮它們的重複性?用'__eq__'表示。 – 2013-03-22 22:09:52

+2

定義相等和散列的最簡單方法是找到一個與您的對象同構的元組,然後通過使用'collections.namedtuple'(如果適用)或通過構造元組來委託給元組的'__hash__'和'__eq__'按需提供:'def __hash __(self):return hash((self.x,self.y,self.z))'。 – delnan 2013-03-22 22:12:12

0

我認爲你可以很容易地做你想要什麼用列表,您在第一篇文章中詢問您自定義了eq運營商:

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

我的2 cts ...

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 

稍微更地道的版本將是一個發電機,而不是返回一個列表的功能。這將使用yield而不是將附加值附加到results變量。我還將lst參數重命名爲iterable,因爲它可以在任何可迭代對象(如另一個生成器)上正常工作。

def dedupe(iterable): 
    seen = set() 
    for item in iterable: 
     if item not in seen: 
      seen.add(item) 
      yield item 
+0

你不必自己寫這個;它已經作爲'unique_everseen'在[itertools食譜](http://docs.python.org/2/library/itertools.html#recipes)中。除了已經寫好,經過充分測試和優化之外,它還需要一個'鍵'功能。所以,只需將其複製到您的代碼並使用它,或者'pip install more-itertools'並從那裏導入它。 – abarnert 2013-03-22 23:11:19

1

您的第一個問題已經由Pavel Anossov回答。

但你有另外一個問題:

但是如何保存順序?設置不支持順序。例如,我可以使用列表而不是集?

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

  • 你得到錯誤的接口。
  • 您不會自動處理重複項。你必須明確寫出if foo not in res: res.append(foo)。顯然,你可以將它封裝在一個函數中,而不是重複編寫它,但它仍然是額外的工作。
  • 如果集合變大,效率會低很多。基本上,添加一個新元素,檢查一個元素是否已經存在,等等都將是O(N)而不是O(1)。

你想要的東西就像是有序的set。或者,等同於不允許重複的list

如果你做你的第一次增加了,然後所有的查找,而你並不需要查找要快,你可以先建立一個list,然後使用unique_everseenitertools recipes刪除重複解決這個問題。

或者你可以只保留通過順序setlist或元素(或list加元素set迄今爲止看到的)。但是這可能會有點複雜,所以你可能想要把它包裝起來。

理想情況下,您希望將其封裝在與set具有完全相同API的類型中。類似OrderedSet類似於collections.OrderedDict

幸運的是,如果您滾動到該文檔頁面的底部,您將看到您確實想要的內容;在ActiveState中有一個鏈接到OrderedSet配方。

因此,複製它,粘貼到您的代碼,然後只需將res = set()更改爲res = OrderedSet(),就完成了。