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

2024-04-25 08:31:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在开发一个模块,它依赖于检查两个列表中是否有任何对象不存在。实现应该在Python中。

考虑简化的对象定义:

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

我需要在属性的基础上找出一个列表中存在而另一个列表中不存在的对象。在这种情况下,下面给出了第一个列表中的项和第二个列表中缺少的项的所需输出。

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

类似地,列表2中的项而不是列表1中的项

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

我想知道是否有一种方法来匹配属性一的基础。

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

Tags: 对象selfnone列表属性foodefone
3条回答

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

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)

不过,如果你有很长的列表,这可能会在计算上相当昂贵。

编辑:只有当你有非常大的列表,因此需要利用集合运算的计算效率时,使用集合才是真正有用的。否则,简单地使用列表理解可能会更简单,正如另一个答案中所建议的那样。

我有两种方法可以做到这一点-要么使用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>]

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

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

相关问题 更多 >