比较Python中的两个生成器

Aka*_*all 24 python

我想知道==在比较两台发电机时的用途

例如:

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

gen_1 = (int(ele) for ele in x)
gen_2 = (int(ele) for ele in x)
Run Code Online (Sandbox Code Playgroud)

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

>>> gen_1 == gen_2
False
Run Code Online (Sandbox Code Playgroud)

我的猜测是,==这里的处理方式与is通常情况相同,因为gen_1gen_2位于内存的不同位置:

>>> gen_1
<generator object <genexpr> at 0x01E8BAA8>
>>> gen_2
<generator object <genexpr> at 0x01EEE4B8>
Run Code Online (Sandbox Code Playgroud)

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

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

>>> all(a == b for a,b in zip(gen_1, gen_2))
True
Run Code Online (Sandbox Code Playgroud)

甚至

>>> list(gen_1) == list(gen_2)
True
Run Code Online (Sandbox Code Playgroud)

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

Sve*_*ach 18

你的猜测是正确的 - 比较未定义的类型的后备==是基于对象身份的比较.

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

from itertools import izip_longest, tee
sentinel = object()
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel))
Run Code Online (Sandbox Code Playgroud)

这实际上可以短路而不必查看所有值.正如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))
Run Code Online (Sandbox Code Playgroud)

这将给出状态gen_1gen_2基本保持不变.消耗的所有值all()都存储在tee对象内.

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


Gre*_*ill 9

因为生成器按需生成它们的值,所以没有任何方法可以"比较"它们而不实际消耗它们.如果您的生成器生成无限的值序列,那么您提出的这种相等性测试将毫无用处.


Fre*_*Foo 6

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

list(gen_1) == list(gen_2)
Run Code Online (Sandbox Code Playgroud)

是比较两个有限生成器的可靠和通用的方法(但显然消耗两者); 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
Run Code Online (Sandbox Code Playgroud)

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


kin*_*all 5

为了对列表和其他容器的两个生成器进行逐项比较,Python必须将它们全部消耗掉(无论如何,较短的生成器)。我认为您必须明确地执行此操作是一件好事,尤其是因为其中一个可能是无限的。