我想知道在比较两个发电机时使用==
例如:
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
但如果有更好的方法,我很想知道。
答案 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_1
和gen_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必须完全消耗它们(好吧,无论如何都要使用它们)。我认为你必须明确地做到这一点很好,特别是因为其中一个可能是无限的。