如何反转一个itertools.chain对象?

11 投票
7 回答
10852 浏览
提问于 2025-04-16 11:53

我的函数创建了一串生成器:

def bar(num):
    import itertools
    some_sequence = (x*1.5 for x in range(num))
    some_other_sequence = (x*2.6 for x in range(num))
    chained = itertools.chain(some_sequence, some_other_sequence)
    return chained

有时候,我的函数需要把这个chained的顺序反过来。简单来说,我想做到的就是下面这样的:

if num < 0:
    return reversed(chained)
return chained

可是:

>>> reversed(chained)
TypeError: argument to reversed() must be a sequence

我有哪些选择呢?

这是一些实时图形渲染的代码,所以我不想让它变得太复杂或慢。

编辑: 当我第一次提出这个问题时,我没有考虑到生成器的可逆性。正如很多人指出的,生成器是不能反向操作的。

我其实想要反转的是链条中展开的内容,而不仅仅是生成器的顺序。

根据大家的反馈,我发现没有一个简单的方法可以直接反转一个itertools.chain,所以我觉得在反转的情况下,唯一的解决办法就是使用列表,可能在两种情况下都需要这样做。

7 个回答

4

itertools.chain 这个工具需要实现 __reversed__() 方法(这样最好),或者实现 __len__()__getitem__() 方法。

但是它并没有这样做,而且你也无法直接访问内部的序列,所以你需要把整个序列展开才能反转它。

reversed(list(CHAIN_INSTANCE))

如果 chain 能在所有序列都可以反转的时候提供 __reversed__() 方法,那就太好了,但目前它并没有这样做。也许你可以自己写一个可以反转的 chain 版本。

11

根据定义,生成器是不能反向操作的。生成器的接口是迭代器,这种迭代器只能向前遍历,不能往回走。如果你想要反向遍历一个迭代器,你必须先把它的所有项目都收集起来,然后再进行反向操作。

可以考虑使用列表,或者从开始就生成反向的序列。

12
if num < 0:
    lst = list(chained)
    lst.reverse()
    return lst
else:
    return chained

reversed() 这个函数需要一个真正的序列,因为它是通过索引反向遍历的,而生成器(generator)只知道“下一个”项,所以用在生成器上是行不通的。

因为你反转的时候反正需要把整个生成器展开,所以最有效的方法是先把它读成一个列表,然后用 .reverse() 方法在原地反转这个列表。

撰写回答