2016-09-21 23 views
0

所以我們說我有這個類:Python的 - 獨立重複的對象爲不同的列表

class Spam(object): 
    def __init__(self, a): 
     self.a = a 

現在我有這些對象:

s1 = Spam((1, 1, 1, 4)) 

s2 = Spam((1, 2, 1, 4)) 

s3 = Spam((1, 2, 1, 4)) 

s4 = Spam((2, 2, 1, 4)) 

s5 = Spam((2, 1, 1, 8)) 

s6 = Spam((2, 1, 1, 8)) 

objects = [s1, s2, s3, s4, s5, s6] 

因此運行某種方法後,我需要有兩個列表的對象在同一列表中具有相同的a屬性值,而其他對象具有唯一的a屬性。

像這樣:

dups = [s2, s3, s5, s6] 
normal = [s1, s4] 

因此,它是像得到複製,但除了它也應該添加對象,甚至第一次出現是股同a屬性值。

我寫了這個方法,它似乎工作,但它在我看來是相當醜陋(並且可能不是非常優化)。

def eggs(objects): 
    vals = [] 
    dups = [] 
    normal = [] 
    for obj in objects: 
     if obj.a in vals: 
      dups.append(obj) 
     else: 
      normal.append(obj) 
      vals.append(obj.a) 
    dups_vals = [o.a for o in dups] 
    # separate again 
    new_normal = [] 
    for n in normal: 
     if n.a in dups_vals: 
      dups.append(n) 
     else: 
      new_normal.append(n) 
    return dups, new_normal 

任何人都可以寫出這樣的問題更合適的Python的方法呢?

回答

2

我會將字詞中的對象組合在一起,使用a屬性作爲關鍵字。然後我會按照團體的大小來區分它們。

import collections 

def separate_dupes(seq, key_func): 
    d = collections.defaultdict(list) 
    for item in seq: 
     d[key_func(item)].append(item) 
    dupes = [item for v in d.values() for item in v if len(v) > 1] 
    uniques = [item for v in d.values() for item in v if len(v) == 1] 
    return dupes, uniques 

class Spam(object): 
    def __init__(self, a): 
     self.a = a 
    #this method is not necessary for the solution, just for displaying the results nicely 
    def __repr__(self): 
     return "Spam({})".format(self.a) 

s1 = Spam((1, 1, 1, 4)) 
s2 = Spam((1, 2, 1, 4)) 
s3 = Spam((1, 2, 1, 4)) 
s4 = Spam((2, 2, 1, 4)) 
s5 = Spam((2, 1, 1, 8)) 
s6 = Spam((2, 1, 1, 8)) 
objects = [s1, s2, s3, s4, s5, s6] 

dupes, uniques = separate_dupes(objects, lambda item: item.a) 
print(dupes) 
print(uniques) 

結果:

[Spam((2, 1, 1, 8)), Spam((2, 1, 1, 8)), Spam((1, 2, 1, 4)), Spam((1, 2, 1, 4))] 
[Spam((1, 1, 1, 4)), Spam((2, 2, 1, 4))] 
1

如果添加__eq__方法Spam,定義爲

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

,那麼你可以用的東西做到這一點很簡單,就像

# you can inline this if you want, just wanted to give it a name 
def except_at(elems, ind): 
    return elems[:ind] + elems[ind+1:] 
dups = [obj for (i, obj) in enumerate(objects) if obj in except_at(objects, i)] 
normal = [obj for (i, obj) in enumerate(objects) if obj not in except_at(objects, i)] 
+0

這似乎把我'類型錯誤:「垃圾郵件」對象未標化的 '我不知道,如果是我的結束,會考慮它,當我在有時間你的'except_at'函數。 – MooingRawr

+0

我的歉意!我將錯誤的變量傳遞給'except_at'。修復。 –

0

使用collections.Counter,這些都是常見的一種以上的按鍵:

你的兩個列表,現在要做到這一點

[o for o in objects if o.a in common], [o for o in objects if o.a not in common] 
0

的一種方式,如果對象的列表不太大,則是對對象列表進行排序,然後應用groupby來獲取重複項。要對列表進行排序,我們提供了一個關鍵函數,用於提取對象的.a屬性的值。

from operator import attrgetter 
from itertools import groupby 

class Spam(object): 
    def __init__(self, a): 
     self.a = a 

    def __repr__(self): 
     return 'Spam({})'.format(self.a) 

s1 = Spam((1, 1, 1, 4)) 
s2 = Spam((1, 2, 1, 4)) 
s3 = Spam((1, 2, 1, 4)) 
s4 = Spam((2, 2, 1, 4)) 
s5 = Spam((2, 1, 1, 8)) 
s6 = Spam((2, 1, 1, 8)) 

objects = [s1, s2, s3, s4, s5, s6] 

keyfunc = attrgetter('a') 

dupe, unique = [], [] 
for k, g in groupby(sorted(objects, key=keyfunc), key=keyfunc): 
    g = list(g) 
    target = unique if len(g) == 1 else dupe 
    target.extend(g) 

print('dupe', dupe) 
print('unique', unique) 

輸出

dupe [Spam((1, 2, 1, 4)), Spam((1, 2, 1, 4)), Spam((2, 1, 1, 8)), Spam((2, 1, 1, 8))] 
unique [Spam((1, 1, 1, 4)), Spam((2, 2, 1, 4))] 
相關問題