我的功能创建了一系列生成器:
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,所以我认为这里唯一的解决方案是使用一个列表,至少在相反的情况下,也许两者都可以。
答案 0 :(得分:10)
if num < 0:
lst = list(chained)
lst.reverse()
return lst
else:
return chained
reversed()
需要一个实际的序列,因为它通过索引向后迭代它,这对于生成器(它只有“next”项的概念)不起作用。
由于您无论如何都需要展开整个生成器以进行反转,因此最有效的方法是将其读取到列表并使用.reverse()
方法就地反转列表。
答案 1 :(得分:9)
您无法按定义反转生成器。生成器的接口是迭代器,它是一个仅支持前向迭代的容器。当你想要反转迭代器时,你必须先收集它的所有项目,然后再反转它们。
改为使用列表或从头开始向后生成序列。
答案 2 :(得分:3)
itertools.chain需要实现__reversed__()
(这是最好的)或__len__()
和__getitem__()
由于它没有,甚至没有办法访问内部序列,您需要扩展整个序列才能反转它。
reversed(list(CHAIN_INSTANCE))
当所有序列都可以反转时,如果链可以使__reversed__()
可用,那将是很好的,但目前它不会这样做。也许你可以编写自己的链条版本
答案 3 :(得分:1)
def reversed2(iter):
return reversed(list(iter))
答案 4 :(得分:0)
reversed
仅适用于支持len
和索引的对象。在将reversed
包裹起来之前,您必须先生成生成器的所有结果。
但是,您可以轻松地执行此操作:
def bar(num):
import itertools
some_sequence = (x*1.5 for x in range(num, -1, -1))
some_other_sequence = (x*2.6 for x in range(num, -1, -1))
chained = itertools.chain(some_other_sequence, some_sequence)
return chained
答案 5 :(得分:0)
这适用于您的真实应用吗?
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))
list_of_chains = [some_sequence, some_other_sequence]
if num < 0:
list_of_chains.reverse()
chained = itertools.chain(*list_of_chains)
return chained
答案 6 :(得分:0)
理论上你不能因为链式对象甚至可能包含无限序列,例如itertools.count(...)
。
您应该尝试反转您的生成器/序列,或者对每个序列使用reversed(iterable)
(如果适用),然后将它们链接到最后一个。当然,这在很大程度上取决于您的使用案例。