用Pythonic的方式通过属性比较两个无序列表的方法

3
什么是最符合Python风格的比较两个无序列表中一个或多个属性的方法?我想知道是否有一种Pythonic的方式,可以找出列表A中的每个项目是否存在一个列表B中的项目,其中列表A和列表B中的该项目在指定属性上匹配。
在我的示例案例中,我有两个.zip文件在一个单元测试中,并希望测试文件是否匹配,但我真正寻找的是我的个人工具集的好通用解决方案。 以下是我的第一次尝试:
with ZipFile('A.zip') as old:
with ZipFile('B.zip') as new:
oldFileInfo = old.infolist()

allFound = True
for info in new.infolist():
   matches = [item for item in oldFileInfo if item.CRC == info.CRC and \   
              basename(item.filename) == basename(info.filename) ]
   if len(matches) == 0:
       allFound = False
       break

也许这很琐碎,但我还没有找到一个好的方法来做到这一点。
问候 Michael

缩进有问题。 - bereal
我同意fedorSmirnov和bereal在他们的回答中所说的。一个关注性能,另一个关注可读性。对于你来说,什么更重要?如果这部分代码被频繁使用,我会选择第一种选项。 - Arthur Julião
1
@ArthurJulião 我认为排序和列表比较不会更快。这两种解决方案都是O(n * log n),但issubset是在C内部实现的,而自定义迭代将在Python中执行。不过还是值得检查一下。 - bereal
是的,我没有看到@bereal提到的那个。 - Arthur Julião
4个回答

2

很简单,你应该使用集合:

if set(list1).difference(set(list2)):
    # lists are different
    # different_items = set(list1).difference(set(list2))
    pass
else:
    # lists are the same
    pass

你可以将结构转换为可迭代对象或列表:
list1 = [(i.CRC, basename(i.filename)) for i in old.infolist()]
list2 = [(i.CRC, basename(i.filename)) for i in new.infolist()]

1
谢谢。这非常清晰简单。但其他的也不错。 - Michael
这种情况有点不对,因为[1,2,3,3,3,3]不应该等于[2,3,1]。 - polvoazul
1
@polvoazul 我不这么认为 - 请仔细阅读问题。列表A中的所有项目[1,2,3,3,3,3]都出现在列表B [2,3,1]中。是的,列表B中的所有项目也出现在列表A中。因此,在这种情况下,这些列表是相等的。如果您想逐一比较列表,请使用len(A) == len(B)作为附加的最高条件。 - Jiri

1
一种可能的方法是:

可以这样做:

def areEqual(old, new):
    set1 = set((x.attribute1, x.attribute2) for x in old)
    set2 = set((x.attribute1, x.attribute2) for x in new)

    return set1 == set2

1

您可以将旧列表和新列表组成集合,然后进行比较:

old_set = set((item.CRC, item.filename) for item in old_info)
new_set = set((item.CRC, item.filename) for item in new_info)

all_match = new_set.issubset(old_set)  # or old_set.issuperset(new_set)

1
你可以从对列表进行排序开始。它的时间复杂度只有n log n,然后你可以逐个比较元素,如果找到不匹配的一对就停止。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接