29

背景

我有一个list. 这list有很多对象。每个对象都有一个id. 现在对象是不同的类型。

objects = [Aobject, Bobject, Cobject]

在哪里

>>> Aobject != Bobject
True
>>> Aobject.id ==  Bobject.id
True

问题

我想要一个list基于object.id.

像这样的东西:

set(objects, key=operator.attrgetter('id'))

(这不起作用。但我想要这样的东西)

4

7 回答 7

35
seen = set() 

# never use list as a variable name
[seen.add(obj.id) or obj for obj in mylist if obj.id not in seen]

这是有效的,因为set.add返回None,所以列表推导中的表达式总是产生obj,但obj.id前提是尚未添加到seen.

(表达式只能计算为Noneif obj is None;在这种情况下,obj.id会引发异常。如果mylist包含None值,请将测试更改为if obj and (obj.id not in seen)

请注意,这将为您提供列表中具有给定 ID 的第一个对象。@Abhijit 的回答会给你最后一个这样的对象。

更新:

或者,ordereddict 可能是一个不错的选择:

import collections
seen = collections.OrderedDict()

for obj in mylist:
    # eliminate this check if you want the last item
    if obj.id not in seen:
       seen[obj.id] = obj

list(seen.values())
于 2012-04-05T07:59:09.303 回答
7

如何使用dict(因为它的键是唯一的)?

假设我们有

class Object:
    def __init__(self, id):
        self.id = id


Aobject = Object(1)
Bobject = Object(1)
Cobject = Object(2)
objects = [Aobject, Bobject, Cobject]

然后可以使用Python 3中的理解生成list具有Object唯一字段的 siddict

unique_objects = list({object_.id: object_ for object_ in objects}.values())

Python 2.7中

unique_objects = {object_.id: object_ for object_ in objects}.values()

Python <2.7中

unique_objects = dict([(object_.id, object_) for object_ in objects]).values()

最后,我们可以编写函数(Python 3版本)

def unique(elements, key):
    return list({key(element): element for element in elements}.values())

whereelements可能是 anyiterable并且key是一些从(在我们的特定情况下等于)callable返回hashable对象的地方。elementskeyoperator.attrgetter('id')

Marcin 的答案工作正常,但对我来说看起来不像 Pythonic,因为列表理解会seen从外部范围改变对象,使用set.add方法并将其结果(即None)与obj.

最后但同样重要的部分:

基准

setup = '''
import random


class Object:
    def __init__(self, id):
        self.id = id


objects = [Object(random.randint(-100, 100))
           for i in range(1000)]
'''
solution = '''
seen = set()
result = [seen.add(object_.id) or object_
          for object_ in objects
          if object_.id not in seen]
'''
print('list comprehension + set: ',
      min(timeit.Timer(solution, setup).repeat(7, 1000)))
solution = '''
result = list({object_.id: object_
               for object_ in objects}.values())
'''
print('dict comprehension: ',
      min(timeit.Timer(solution, setup).repeat(7, 1000)))

在我的机器上给

list comprehension + set:  0.20700953400228173
dict comprehension:  0.1477799109998159
于 2018-03-08T08:49:43.853 回答
3

鉴于您的对象列表somelist类似于

[(Object [A] [1]), (Object [B] [1]), (Object [C] [2]), (Object [D] [2]), (Object [E] [3])]

你可以做这样的事情

>>> {e.id:e for e in somelist}.values()
[(Object [B] [1]), (Object [D] [2]), (Object [E] [3])]
于 2012-04-05T08:24:45.667 回答
0

如果您可以更改对象的类,则可以添加用于集合比较的适当方法:

# Assumption: this is the 'original' object
class OriginalExampleObject(object):
    def __init__(self, name, nid):
        self.name = name
        self.id = nid
    def __repr__(self):
        return "(OriginalExampleObject [%s] [%s])" % (self.name, self.id)

class SetExampleObj(OriginalExampleObject):
    def __init__(self, name, nid):
        super(SetExampleObj, self).__init__(name, nid)
    def __eq__(self, other):
        return self.id == other.id
    def __hash__(self):
        return self.id.__hash__()


AObject = SetExampleObj("A", 1)
BObject = SetExampleObj("B", 1)
CObject = SetExampleObj("C", 2)

s = set()
s.add(AObject)
s.add(CObject)
print(s)

s.add(BObject)
print(s)

输出:

set([(OriginalExampleObject [A] [1]), (OriginalExampleObject [C] [2])])
set([(OriginalExampleObject [A] [1]), (OriginalExampleObject [C] [2])])
于 2012-04-05T08:14:22.850 回答
0

您可以使用文档unique_everseen中提供的配方。这在 3rd 方库中也可用,例如. 请注意,此方法将保留给定属性的对象的第一个实例。itertools toolz.unique

from toolz import unique
from operator import attrgetter

res = list(unique(objects, key=attrgetter('id')))

如果惰性迭代器就足够了,则可以省略list转换。

于 2018-09-27T14:46:23.923 回答
-1

一个相当简单的方法是

for obj in mylist:
    if obj.id not in s:
        s.add(obj.id)

这应该添加任何未见过的 id。花费的时间与源列表的大小成线性关系。

于 2012-04-05T08:11:04.380 回答
-1
objects = [Aobject, Bobject, Cobject]
unique_objects = {o['id']:o for o in objects}.values()
于 2019-09-19T06:06:35.267 回答