比较Python中的两个生成器

时间:2012-04-02 20:44:19

标签: python

我想知道在比较两个发电机时使用==

例如:

x = ['1','2','3','4','5']

gen_1 = (int(ele) for ele in x)
gen_2 = (int(ele) for ele in x)

gen_1 gen_2 对于所有实际用途都是相同的,但是当我比较它们时:

>>> gen_1 == gen_2
False

我的猜测是==此处的处理方式与is相同,因为 gen_1 gen_2 位于不同的地方存储器:

>>> gen_1
<generator object <genexpr> at 0x01E8BAA8>
>>> gen_2
<generator object <genexpr> at 0x01EEE4B8>

他们的比较评估为False。我猜对了吗?欢迎任何其他见解。

顺便说一下,我知道如何比较两个发电机:

>>> all(a == b for a,b in zip(gen_1, gen_2))
True

甚至

>>> list(gen_1) == list(gen_2)
True

但如果有更好的方法,我很想知道。

4 个答案:

答案 0 :(得分:17)

您的猜测是正确的 - 比较未定义==的类型的后备是基于对象标识的比较。

比较它们生成的值的更好方法是

from itertools import izip_longest, tee
sentinel = object()
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel))

这实际上可以短路而不必查看所有值。正如larsmans在评论中指出的那样,我们不能在这里使用izip(),因为如果生成器生成不同数量的元素,它可能会给出错误的结果 - izip()将停在最短的迭代器上。我们使用新创建的object实例作为izip_longest()的填充值,因为object实例也按对象标识进行比较,因此sentinel保证与其他所有内容进行比较。

请注意,无法在不更改状态的情况下比较生成器。如果以后需要,您可以存储已消耗的项目:

gen_1, gen_1_teed = tee(gen_1)
gen_2, gen_2_teed = tee(gen_2)
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel))

这将使gen_1gen_2的状态基本保持不变。 all()消耗的所有值都存储在tee对象中。

此时,您可能会问自己,对于手头的应用程序使用延迟生成器是否真的值得 - 将它们简单地转换为列表并使用列表可能更好。

答案 1 :(得分:9)

因为生成器按需生成它们的值,所以没有任何方法可以“比较”它们而不实际消费它们。如果您的生成器生成无限的值序列,那么您提出的这样的等式测试将毫无用处。

答案 2 :(得分:6)

==确实与两个生成器上的is相同,因为这是唯一可以在不改变状态而丢失元素的情况下进行的检查。

list(gen_1) == list(gen_2)

是比较两个有限生成器的可靠而通用的方法(但显然消耗两者);基于zip的解决方案在没有生成相同数量的元素时失败:

>>> list(zip([1,2,3,4], [1,2,3]))
[(1, 1), (2, 2), (3, 3)]
>>> all(a == b for a, b in zip([1,2,3,4], [1,2,3]))
True

当任一生成器生成无限数量的元素时,基于list的解决方案仍然失败。您可以为此设计一种解决方法,但是当两个生成器都是无限的时,您只能为不相等设置semi-algorithm

答案 3 :(得分:5)

为了像列表和其他容器一样对两个生成器进行逐项比较,Python必须完全消耗它们(好吧,无论如何都要使用它们)。我认为你必须明确地做到这一点很好,特别是因为其中一个可能是无限的。