查找两个列表中不存在的对象的最佳方法

时间:2013-02-11 07:23:55

标签: python sorting data-structures

我正在研究一个模块,该模块依赖于检查2个列表中是否存在任何对象。该实现应该是Python。

考虑简化的对象def:

class Foo(object):

  def __init__(self, attr_one=None, attr_two=None):
    self.attr_one = attr_one
    self.attr_two = attr_two

  def __eq__(self, other):
    return self.attr_one == other.attr_one and self.attr_two == other.attr_two

我有两个单独的列表可以封装类Foo的多个实例,如下所示:

list1 = [Foo('abc', 2), Foo('bcd', 3), Foo('cde', 4)]
list2 = [Foo('abc', 2), Foo('bcd', 4), Foo('efg', 5)]

我需要弄清楚一个列表中存在的对象,而另一个列表中的对象基于attr_one。在这种情况下,第一个列表中存在且第二个列表中缺少的项目的所需输出如下所示。

`['Foo('bcd', 3), Foo('cde', 4)]` 

类似地,列表2中但不在列表1中的项目

 [Foo('bcd', 4), Foo('efg', 5)]

我想知道是否有办法匹配attr_one的基础。

  List 1                 List 2        
  Foo('bcd', 3)          Foo('bcd', 4)
  Foo('cde', 4)          None
  None                   Foo('efg', 5)

3 个答案:

答案 0 :(得分:8)

由于您已经定义了__eq__方法,因此您可以使用列表推导来查找任一列表中对象的唯一性。

print [obj for obj in list1 if obj not in list2]

答案 1 :(得分:3)

快速比较列表以确定哪一个元素存在于另一个元素中而不是另一个元素的好方法是从原始列表创建集合并获取两个集合之间的差异。为了将列表设置为一个集合,它包含的对象必须是hashable,因此您必须为__hash__()个对象定义新的Foo方法:

def __hash__(self):
    return hash((self.attr_one,self.attr_two))

请注意,由于元组是可清除的,只要attr_oneattr_two是可清除类型,此实现就应该非常可靠。

现在,要确定哪个元素存在于一个列表中而不存在于另一个列表中:

set1 = set(list1)
set2 = set(list2)
missing_from_1 = set2 - set1
missing_from_2 = set1 - set2

要仅基于其中一个属性执行此操作,您可以仅使用属性本身创建集合:

set1 = set([i.attr_one for i in list1])

当然,这意味着您最终会得到的结果只会告诉您一个列表中存在的attr_one值,而不会告诉您另一个列表中的Foo值,而不是为您提供实际的missing_Foos = set() for attr in missing_from_2: for i in list1: if i.attr_one == attr: missing_Foos.add(i) 个对象。然而,一旦你有“缺失”的集合,对象本身很容易找到:

{{1}}

但是,如果您有很长的列表,这可能会在计算上相当昂贵。

编辑:如果你有非常大的列表,那么使用集合非常有用,因此需要利用集合操作的计算效率。否则,简单地使用列表推导可能更简单,如另一个答案所示。

答案 2 :(得分:1)

我有两种方法可以执行此操作 - 使用sets或使用filter

class Foo(object):

    def __init__(self, attr_one=None, attr_two=None):
        self.attr_one = attr_one
        self.attr_two = attr_two

    def __eq__(self, other):
        return self.attr_one == other.attr_one and self.attr_two == other.attr_two

    def __hash__(self):
        return hash(self.attr_one)

    def __repr__(self):
        return "<Foo {} {}>".format(self.attr_one, self.attr_two)

def main():
    a = Foo('test', 1)
    b = Foo('test', 1)

    list1 = [Foo('abc', 2), Foo('bcd', 3), Foo('cde', 4)]
    list2 = [Foo('abc', 2), Foo('bcd', 4), Foo('efg', 5)]

    # With sets
    list1set = set(list1)
    list2set = set(list2)

    print list1set.intersection(list2set) 
    # Returns set([<Foo abc 2>])

    # With filter
    list2attr_one = [l.attr_one for l in list2]
    print filter(lambda x: x.attr_one in list2attr_one, list1)
    # Returns [<Foo abc 2>, <Foo bcd 3>]