2011-02-15 22 views
9

我的函数创建发电机的链条:如何反转itertools.chain对象?

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

哼的版本,我还以为'itertools.islice`带有负一步会工作,但事实证明,只有正值是有效的是对Arg的` islice`。有趣的问题。 – 2011-02-15 20:13:15

+0

这不仅仅是'链接',它是你的生成器: >>>颠倒((x对于范围(5)中的x))` `TypeError:参数到颠倒()必须是一个序列` – 2011-02-15 20:13:34

+0

@jleedev好点;即使有办法翻转itertools.chain,也不能逆转生成器。我以前没有意识到,但现在明白了。但是,反转也不适用于仅由列表组成的itertools.chain。 – 2011-02-15 22:03:13

回答

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

reversed()需要一个实际的序列,因为它遍历它向后通过索引,而不会为发电机工作(其中只有“未来”项目的概念)。

由于无论如何您需要展开整个发生器以进行反转,因此最有效的方法是将其读取到列表中,并使用.reverse()方法就地反转列表。

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 
+0

实现`__reversed__`的对象也可以使用`reversed()`:https://docs.python.org/2/reference/datamodel.html#object.__reversed__ – 2017-08-16 15:17:04

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 
7

您无法按照定义反转生成器。生成器的接口是迭代器,它是仅支持前向迭代的容器。当你想要颠倒一个迭代器时,你必须首先收集所有的项目,然后再将它们反转。

使用列表代替或向后从一开始就产生的序列。

0

理论上你不能因为连锁目标甚至可能含有无穷序列,如itertools.count(...)

你应该尝试扭转你的发电机/序列或使用reversed(iterable)每个序列(如果适用),然后把它们连在一起,最后以诚为先。当然,这很大程度上取决于你的用例。

3

itertools。链将需要实现__reversed__()(这将是最好的)或__len__()__getitem__()

因为它没有,甚至没有办法访问内部序列,你需要扩大整个序列,以便能够扭转它。

reversed(list(CHAIN_INSTANCE)) 

这将是很好,如果链将使__reversed__()可当所有的序列是可逆式,但目前它没有做到这一点。也许你可以编写自己的链,做

1
def reversed2(iter): 
    return reversed(list(iter))