HOWTO · Python

如何在 Python 中比较列表

根据是否在意顺序和重复次数,使用 `==`、`Counter`、集合操作或保留顺序的列表推导式来比较 Python 列表。

本页内容

比较两个 Python 列表时,先确定顺序和重复次数是否有意义。精确且有序的相等性使用 ==;忽略顺序但保留重复次数时使用 Counter;只关心唯一值的交集或差集时使用 set();结果必须按输入列表顺序排列时使用列表推导式。

下文所有示例都只使用 Python 标准库,并已用 Python 3.14.7 验证。除非项目另有兼容性要求,这些方法同样适用于受支持的较早 Python 3 版本。

目标 推荐方法 顺序是否重要 重复次数是否重要 元素要求
测试完全相等的列表 left == right 是 是 元素必须支持相等比较
测试无序相等性 Counter(left) == Counter(right) 否 是 元素必须可哈希
比较唯一值 set() 操作 否 否 元素必须可哈希
按列表顺序保留共同元素 列表推导式加成员集合 只影响输出顺序 保留被扫描列表中的重复项 成员值必须可哈希
在交集或差集中保留数量 Counter 操作 否 是 元素必须可哈希

这些方法回答的是不同问题,并不是在争夺同一个“最快”方案。相等比较返回一个布尔值,集合和 Counter 操作会构造类似集合的结果,而推导式能控制新列表的形状和顺序。先决定所需语义再考虑性能;仅为加快比较而更换表示方式,也可能改变答案。

使用 == 比较完全有序的列表

Python 的列表相等比较会从左到右比较对应元素。只有长度相同、每个位置的值相等且顺序一致时,两个列表才相等。当列表表示处理步骤、排名结果或有序事件等序列时,这是最清晰的选择。

"""Verify that list equality considers both values and order."""

first = [1, 2, 3]
same = [1, 2, 3]
reordered = [3, 2, 1]

print(first == same)
print(first == reordered)
True
False

第二个比较即使两个列表包含相同的唯一值,结果仍为 False。直接相等比较也适用于嵌套列表等不可哈希元素,因为它不会构建哈希表。

元素比较仍遵循各个值自身的相等规则。例如,比较自定义对象组成的列表可能会调用对象的 __eq__ 实现。此方法不会递归指出嵌套结构在哪一处不同;它只返回最终的布尔结果。

使用 Counter 比较保留重复次数的无序列表

当需要忽略顺序但保留每个值出现的次数时,请使用 collections.Counter。Counter 将每个可哈希项目映射为计数,因此只有两个列表包含相同值且每个值的出现次数相同时,两个计数器才相等。

"""Compare unordered lists with and without duplicate multiplicity."""

from collections import Counter

left = [1, 2, 2, 3]
reordered = [3, 2, 1, 2]
fewer_duplicates = [3, 2, 1]

print(Counter(left) == Counter(reordered))
print(Counter(left) == Counter(fewer_duplicates))
print(set(left) == set(fewer_duplicates))
True
False
True

最后两行揭示了一个重要边界:Counter 能发现少了第二个 2,而集合比较会报告相等,因为集合会丢弃重复项。重复值有意义时,不要用 set(left) == set(right) 代替。

内置 sorted() 还提供了保留重复次数的另一种选择:sorted(left) == sorted(right),但前提是列表中的所有值可以彼此排序。它还会分配排序后的列表并执行排序,因此 Counter 通常能更直接地表达无序频率相等性。若之后还需要排序后的序列,排序仍然有用。

使用 set() 比较唯一值、交集和差集

只关心不同值时,将列表转换为集合。集合交集(&)返回同时存在于两个输入中的值。差集(-)具有方向性,而对称差集(^)返回只出现在一侧的值。

下面经过验证的示例只对展示出的集合结果排序,以使输出具有确定性。比较本身并不会为集合赋予顺序。

集合转换适合需要大量成员查询的任务,因为它会一次创建查找结构;但输出必须保留位置或重复次数时并不适合。也不要依赖集合的打印顺序;只有在值能够相互排序且需要稳定展示时,才显式排序。

"""Verify unique-value, order-preserving, and multiplicity-aware differences."""

from collections import Counter

left = [1, 2, 2, 3, 4]
right = [2, 3, 3, 5]
left_set = set(left)
right_set = set(right)

print(sorted(left_set & right_set))
print(sorted(left_set - right_set))
print(sorted(right_set - left_set))

right_members = set(right)
print([item for item in left if item in right_members])

left_counts = Counter(left)
right_counts = Counter(right)
print(sorted((left_counts & right_counts).elements()))
print(sorted((left_counts - right_counts).elements()))
print(sorted((right_counts - left_counts).elements()))
[2, 3]
[1, 4]
[5]
[2, 2, 3]
[2, 3]
[1, 2, 4]
[3, 5]

前三行输出展示唯一值的交集和有方向的差集。如果还需要恰好出现在一个集合中的值,请使用 left_set ^ right_set;对这些输入,它会以未指定顺序产生唯一值 1、4 和 5。

使用列表推导式保留结果顺序

集合结果不会保留第一个列表的顺序。输出顺序重要时,先建立一次成员集合,再扫描需要保留顺序的列表。在前面的示例中,right_members 在推导式之前只创建一次,而不是为 left 中的每个元素重建集合或线性扫描第二个列表。

第四行输出 [2, 2, 3] 保留了 left 的顺序和重复出现的元素。这个结果有意是不对称的:改为扫描 right 就会保留 right 的顺序和重复项。若成员值不可哈希,可对另一个列表使用基于相等性的直接成员检查,但要知道大列表上的重复扫描可能成本更高。

使用 Counter 保留交集和差集中的次数

集合操作回答的是唯一值问题。Counter 操作回答同类问题,同时保留计数:

  • left_counts & right_counts 为每个共同值保留最小的正计数。
  • left_counts - right_counts 扣除计数后只保留正的余数。
  • right_counts - left_counts 给出相反方向的差集。
  • .elements() 将结果计数重新展开为单独的值。

在已验证的示例中,多重集合交集是 [2, 3],不是 [2, 2, 3]:2 在左侧出现两次、右侧只出现一次,所以最小计数为一。左侧的有方向余数是 [1, 2, 4],右侧余数是 [3, 5]。

对于库存、投票、带重复的标签或其他数量有意义的数据,请选择此方法。普通集合会悄悄抹掉这些信息。

Counter 算术会从这些多重集合结果中移除零和负计数。这对“剩余项目”问题很有帮助,但不同于对每个可能键进行普通数值减法。若应用必须保留零或负的余额,请直接检查计数器。

处理不可哈希的值并选择正确方法

set() 和 Counter 都要求元素可哈希。列表可变且不可哈希,因此嵌套列表会触发 TypeError;直接列表相等比较仍可正常工作:

"""Show the unhashable-item boundary of set and Counter comparisons."""

from collections import Counter

left = [[1], [2]]
right = [[1], [2]]

print(left == right)
for name, operation in (("set", set), ("Counter", Counter)):
    try:
        operation(left)
    except TypeError as error:
        print(f"{name}: {error}")
True
set: cannot use 'list' as a set element (unhashable type: 'list')
Counter: unhashable type: 'list'

set 的确切诊断信息可能随 Python 版本变化,因此应将异常类型和元素不可哈希这一原因视为稳定行为。若嵌套序列可以用不可变形式表示,请在使用集合或 Counter 前把每个内部列表转换为元组;否则,继续使用符合所需结果的基于相等性的比较。

简言之,选择符合数据含义的表示方式:有序序列用 ==,无序多重集合用 Counter,唯一值用 set(),筛选输出用保留顺序的推导式。明确顺序和重复规则,才能避免看似正确却回答了错误问题的比较。