Python - 将重复的对象分成不同的列表

时间:2016-09-21 13:38:09

标签: python duplicates unique

所以,我要说这堂课:

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

有人能为这样的问题编写更合适的pythonic方法吗?

4 个答案:

答案 0 :(得分: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 :(得分:1)

如果您将int len = strlen(sentence); for (i=0; i<len; i++) 方法添加到__eq__,则定义为

Spam

那么你可以用

之类的东西很简单地做到这一点
def __eq__(self, other):
    return self.a == other.a

答案 2 :(得分:0)

使用collections.Counter,这些是多个共同的键:

import collections

common = [k for (k, v) in collections.Counter([o.a for o in objects]).items() if v > 1]

你的两个名单现在是

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

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