如何检查 Python 中的可迭代对象是否相等?
如何检查 Python 中的可迭代对象是否相等?
1. 简单的方式
假设有两个列表,想知道这两个列表中的元素是否相同,可以使用相等运算符(==)判断:
>>> lines1 = ["Grains", "Kindred", "Zia"]
>>> lines2 = ["Grains", "Kindred", "Zia"]
>>> lines1 == lines2
True
用同样的方法来判断两个元组是否相等:
>>> p = (3, 4, 8)
>>> q = (3, 5, 7)
>>> p == q
False
如果判断一个列表和一个元组(但是它们都是可迭代对象)是否相等呢?肯定返回 False
。
>>> lines1 = ["Grains", "Kindred", "Zia"]
>>> lines2 = ("Grains", "Kindred", "Zia")
>>> lines1 == lines2
False
2. 比较不同类型的可迭代对象
为了比较元组和列表中的元素是否相等,可以将元组转换为列表或者将列表转换为元组。
>>> lines1 = ["Grains", "Kindred", "Zia"]
>>> lines2 = ("Grains", "Kindred", "Zia")
>>> lines1 == list(lines2)
True
这种方法也能用于任何其他类型的可迭代对象。
例如,有两个 itertools.chain
对象:
>>> file1a = open("year1-first.txt")
>>> file1b = open("year1-second.txt")
>>> file2a = open("year2-first.txt")
>>> file2b = open("year2-second.txt")
>>> from itertools import chain
>>> year1 = chain(file1a, file1b)
>>> year2 = chain(file2a, file2b)
上面得到的 chain
对象是可迭代的,也是迭代器对象。这种迭代器对象,不能直接用 ==
判断是否相等。但是如果循环这两个对象,可以观察到其中的元素的确是相同的。
>>> year1 == year2
False
而如果用下面的方式,则能够进行比较(本质上是已经循环读取到了两个迭代器中的元素)。
>>> list(year1) == list(year2)
True
3. 检查两个很大的可迭代对象
由以上可知,可以将可迭代对象转换为列表或元组,然后用 ==
判断它们是否相等。但是,如果要比较两个非常大的可迭代对象,并且是前几个元素不同,那么,用上面的方式进行比较,则要消耗更多的内存和时间。
例如,也许我们正在比较两个文件,而这两个文件通常要么完全相同,要么在前几行内就会出现差异。
可以一次性将整个文件读入内存,然后逐个进行比较:
with open("results1.csv") as file1, open("results2.csv") as file2:
same = (file1.read() == file2.read())
或者用 zip
函数,其参数中设置 strict = True
,意思是迭代地逐行比较:
with open("results1.csv") as file1, open("results2.csv") as file2:
same = True
for line1, line2 in zip(file1, file2, strict=True):
if line1 != line2:
same = False
break
这种检查非匹配值、设置布尔值并跳出循环的风格,实际上正是 Python 的 any
和 all
函数的用武之地。
因此,我们可以通过使用生成器表达式和内置的 all
函数来实现相同的功能:
with open("results1.csv") as file1, open("results2.csv") as file2:
same = all(
line1 == line2
for line1, line2 in zip(file1, file2, strict=True)
)
无论哪种方式,我们都是逐行遍历每个文件,比较每一行,从而判断它们相等,然后在完成比较后丢弃之前的内容,并在发现不相等的行时立即停止。
3. 近似相等
这种使用 all
和 zip
的方法也可以调整为用于检查近似相等。
例如,假设读取两个 CSV 文件并进行比较,但只关心每行的第一列是否在文件中匹配:
import csv
with open("results1.csv") as file1, open("results2.csv") as file2:
reader1, reader2 = csv.reader(file1), csv.reader(file2)
same = all(
row1[0] == row2[0]
for row1, row2 in zip(reader1, reader2, strict=True)
)
使用 zip
和 all
的比较方式非常适合于不关心要比较的可迭代对象的类型,只关心它们是否以相同顺序包含相同元素的情况。因此,如果想要检查一个列表和一个元组(或任何其他可迭代对象)是否包含相同的元素,就可以这样做。
4. 比较可迭代对象时忽略顺序
到目前为止,所有这些方法都假设两个可迭代对象中的元素的顺序是重要的。但如果在比较两个可迭代对象时忽略它们元素的顺序,该怎么办呢?
>>> members = ["Gregory", "Barry", "Pablo", "Emily", "Donghee"]
>>> speakers = ["Barry", "Donghee", "Emily", "Gregory", "Pablo"]
如果元素是字符串、数字,或者其他可哈希的对象,可以将可迭代对象转换为集合,然后比较。
>>> set(members) == set(speakers)
True
如果可迭代对象中包含重复对象,并且这种重复是必须的,可以使用 collections.Counter
:
>>> hand1 = ["AS", "AS", "KS", "KH", "QC"]
>>> hand2 = ["AS", "KH", "AS", "QC", "KS"]
>>> from collections import Counter
>>> Counter(hand1) == Counter(hand2)
True
如果元素是可排序的,也可以使用 sorted
函数:
>>> sorted(hand1) == sorted(hand2)
True
但排序的过程比向 Counter
对象添加元素要慢,因此更倾向于使用 Counter
而不是 sorted
。
5. 比较可迭代对象不仅仅是检查相等性
Python 喜欢鸭子类型(duck typing),这意味着通常更关注对象的行为,而不是对象的类型。
正因为如此,编写能够处理列表的代码,同时也能处理其他类型的可迭代对象是很常见的。因此,有时检查两个可迭代对象是否包含相同的元素,涉及的内容可能比简单的相等性检查更多。
作者:CS创新实验室