【问题标题】:python: comparing 2 lists of instancespython:比较2个实例列表
【发布时间】:2013-01-21 04:17:04
【问题描述】:

我有 2 个实例列表:

list1
list2

每个实例都包含 id、name 等变量...

我正在遍历 list2,我想查找 list1 中不存在的条目。

例如..

for entry in list2:
  if entry.id in list1:
    <do something> 

我希望找到一种方法来做到这一点,而无需双重 for 循环。有什么简单的方法吗?

【问题讨论】:

  • 实例是什么类型的?他们是否实现了__hash__
  • 从我假设的问题来看,您在 2 个列表中有一个类的对象列表,并希望根据您的条件获得一个过滤列表

标签: python list class search instance


【解决方案1】:

大概是这样的吧?

In [1]: list1 = [1,2,3,4,5]

In [2]: list2 = [4,5,6,7]

In [3]: final_list = [x for x in list1 if x not in list2]

【讨论】:

    【解决方案2】:

    您可以使用filter

    difference = filter(lambda x: x not in list1, list2)
    

    在 Python 2 中,它将返回您想要的列表。在 Python 3 中,它将返回一个filter 对象,您可能希望将其转换为列表。

    【讨论】:

    • 这里没有理由避免列表理解——difference = [ x for x in list2 if x not in list1 ]。这样做的好处是它在 python3 和 python2 上给你同样的东西——但不要自欺欺人,这仍然和 OP 的原始代码一样是一个“双循环”。
    【解决方案3】:

    我可能会这样做:

    set1 = set((x.id,x.name,...) for x in list1)
    difference = [ x for x in list2 if (x.id,x.name,...) not in set1 ]
    

    其中... 是实例的附加(可散列)属性——您需要包含足够多的属性以使其唯一。

    这会将您的 O(N*M) 算法转换为 O(max(N,M)) 算法。

    【讨论】:

    • 谢谢,这对我来说非常有效。最后我只需要 id 因为这已经是唯一的了
    【解决方案4】:

    只是一个想法......

    class Foo(object):
        def __init__(self, id, name):
            self.id = id
            self.name = name
        def __repr__(self):
            return '({},{})'.format(self.id, self.name)
    
    list1 = [Foo(1,'a'),Foo(1,'b'),Foo(2,'b'),Foo(3,'c'),]
    list2 = [Foo(1,'a'),Foo(2,'c'),Foo(2,'b'),Foo(4,'c'),]
    

    所以通常这不起作用:

    print(set(list1)-set(list2))
    # set([(1,b), (2,b), (3,c), (1,a)])
    

    但是你可以教Foo 两个实例相等意味着什么:

    def __hash__(self):
        return hash((self.id, self.name))
    
    def __eq__(self, other):
        try:
            return (self.id, self.name) == (other.id, other.name)
        except AttributeError:
            return NotImplemented
    
    Foo.__hash__ = __hash__
    Foo.__eq__ = __eq__
    

    现在:

    print(set(list1)-set(list2))
    # set([(3,c), (1,b)])
    

    当然,您更有可能在类定义时在Foo 上定义__hash____eq__,而不需要稍后对其进行猴子补丁:

    class Foo(object):
        def __init__(self, id, name):
            self.id = id
            self.name = name
    
        def __repr__(self):
            return '({},{})'.format(self.id, self.name)
    
        def __hash__(self):
            return hash((self.id, self.name))
    
        def __eq__(self, other):
            try:
                return (self.id, self.name) == (other.id, other.name)
            except AttributeError:
                return NotImplemented
    

    为了满足我自己的好奇心,这里有一个基准:

    In [34]: list1 = [Foo(1,'a'),Foo(1,'b'),Foo(2,'b'),Foo(3,'c')]*10000
    
    In [35]: list2 = [Foo(1,'a'),Foo(2,'c'),Foo(2,'b'),Foo(4,'c')]*10000
    In [40]: %timeit set1 = set((x.id,x.name) for x in list1); [x for x in list2 if (x.id,x.name) not in set1 ]
    100 loops, best of 3: 15.3 ms per loop
    
    In [41]: %timeit set1 = set(list1); [x for x in list2 if x not in set1]
    10 loops, best of 3: 33.2 ms per loop
    

    所以@mgilson 的方法更快,虽然在Foo 中定义__hash____eq__ 会导致代码更具可读性。

    【讨论】:

    • 请注意,您可能希望对散列函数更加小心。尝试:a = Foo(1,'a'); set([a,hash(a)]) 与 cpython 2.7.3,你会得到一个属性错误,因为你不能比较 Fooint 对象。
    • @mgilson:谢谢;我添加了一些代码(到__eq__)来解决这种可能性。如果您有其他想法或看到改进,请告诉我。
    • 我认为与其提出TypeError,不如直接返回False。不同类型的对象在 python 中总是比较不相等(除非你覆盖 __eq__ 以使其不相等)。我认为您将 == 与其他所谓的丰富比较 (&gt;=) 混淆了。在 python3 中,你不能对不同类型的对象使用丰富的比较,但你可以仍然使用==。也许在这种情况下,提高NotImplemented 是可以的,因为它会退回到对方的比较上,会说“不,这不对……”
    • @mgilson:哎呀!你是绝对正确的。在 Python2 和 Python3 中,相等性测试只返回 TrueFalse。我会删除无用的废话。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多