遍历生成器时,每次取两个项目。

7

我定义了一个生成器,从Elasticsearch中产生日志条目:

def gen():
    ....
    for hit in results:
        yield hit

我该如何同时循环遍历两个元素?类似以下代码实现:
for one, two in gen():
    ...

所谓两个元素指的是:A,BB,C,...,Y,Z(对于生成的列表A,B,...,Y,Z)。

3个回答

10

此答案假设您想要非重叠的一对。您可以通过使用zip()来实现,因为迭代器会被消耗:

for one, two in zip(gen, gen):
    # do something

示例:

>>> gen = (x for x in range(5))
>>> for one, two in zip(gen, gen): print(one,two)
... 
0 1
2 3

注意,正如timgeb所评论的那样,如果你的元素数量不均匀并且你想要最后一个带有填充值的元素,则应使用itertools.zip_longest,例如:

>>> gen = (x for x in range(5))
>>> for one, two in zip_longest(gen, gen): print(one, two)
... 
0 1
2 3
4 None

这会确保元素的相同顺序并且重复使用先前加载的项目吗? - linkyndy
2
请注意,如果从生成器中产生的对象数量不均,则此操作将无法产生最后一个元素。在这种情况下,使用 itertools.izip_longest 将会得到 (last_element, None)(或任何您想要的填充值)。 - timgeb
@AndreiHorak,您是什么意思,“重复使用之前加载的项目”? - timgeb
1
@timgeb 谢谢,没错,我选择了 zip_longest(假设是 Python 3)。 - Chris_Rands
@AndreiHorak 我明白了,根据你的更新说明,使用timgeb刚刚发布的答案(我也在考虑类似的更新) - Chris_Rands

7
回答您更新的问题,使用 itertools.tee 来构建第二个迭代器,将第二个迭代器推进一次并丢弃结果,然后使用 zip 在成对的情况下循环遍历两个迭代器。
>>> from itertools import tee
>>> it = iter('abc')
>>> it1, it2 = tee(it)
>>> 
>>> next(it2, None)
'a'
>>> for first, second in zip(it1, it2):
...     first, second
... 
('a', 'b')
('b', 'c')

感谢您。这是最干净、最符合Python风格的方法吗?我觉得解决这个简单问题的方案太复杂了。
我认为没有更干净的解决方案了。事实上,这是从itertools文档中的“pairwise”配方中取出的。
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

谢谢。这是最干净、最符合“Python风格”的方法吗?我觉得解决这个简单问题的方案太复杂了。 - linkyndy
1
@AndreiHorak 没问题,请确保如果您使用 Python 2,则像食谱中一样使用 izip,特别是如果您的迭代器/生成器可以生成无限数量的对象。 - timgeb
如果只有一个迭代器,使用“tee”是不错的选择,但如果你知道你直接使用的是列表或其他容器,那么你可以使用两个iter来调用,像这样:it1, it2 = iter(my_list), iter(my_list) - juanpa.arrivillaga
虽然我错过了这些是 Elasticsearch 的日志记录,所以“tee”才是正确的方式! - juanpa.arrivillaga

0
我曾经用以下方法解决了类似的问题:
def gen():
    results = [1, 2, 3, 4, 5]
    for i, v in enumerate(results):
        # if i < len()
        if (i + 1) < len(results):
            yield (v, results[i + 1])
        else:
            yield v


output = gen()

for each in output:
    print(each)

输出将会是:

(1, 2)
(2, 3)
(3, 4)
(4, 5)
5

由于生成器循环遍历分页的 Elasticsearch 结果,因此我认为在那里将所有内容加载到内存中以便两两分组并不是最优解。 - linkyndy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接