Python-将重复对象分离到不同的列表中

Python-将重复对象分离到不同的列表中,python,duplicates,unique,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

假设我有一门课:

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
属性

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)
像这样:

dups = [s2, s3, s5, s6]
normal = [s1, s4]
所以这有点像获取重复项,但除此之外,它还应该添加共享相同
属性值的对象的第一次出现

我已经写了这个方法,它似乎是有效的,但在我看来,它是相当丑陋的(可能不是很理想)


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

我会将字典中的对象分组,使用
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))]

我会使用
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))]

如果将
方法添加到
垃圾邮件
,定义为

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)]

如果将
方法添加到
垃圾邮件
,定义为

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)]
使用,以下是多个公用键:

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]
使用,以下是多个公用键:

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]

如果对象列表不太大,一种方法是对对象列表进行排序,然后对其应用
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))]

如果对象列表不太大,一种方法是对对象列表进行排序,然后对其应用
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))]

这似乎给我带来了
TypeError:“垃圾邮件”对象不可订阅
我不知道它是否在我这边,当我有时间使用你的
功能时,我会调查它,除了
功能。我很抱歉!我将错误的变量传递到
中,但在
处除外。修复了它。这似乎让我陷入了
类型错误:“垃圾邮件”对象不可订阅
我不知道它是否在我这边,当我有时间使用你的
功能时,我会调查它,除了
功能。我道歉!我将错误的变量传递到
中,但在
处除外。修好了。