如何检查 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 的 anyall 函数的用武之地。

因此,我们可以通过使用生成器表达式和内置的 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. 近似相等

这种使用 allzip 的方法也可以调整为用于检查近似相等。

例如,假设读取两个 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)
    )

使用 zipall 的比较方式非常适合于不关心要比较的可迭代对象的类型,只关心它们是否以相同顺序包含相同元素的情况。因此,如果想要检查一个列表和一个元组(或任何其他可迭代对象)是否包含相同的元素,就可以这样做。

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创新实验室

物联沃分享整理
物联沃-IOTWORD物联网 » 如何检查 Python 中的可迭代对象是否相等?

发表回复