How do I reverse an itertools.chain object?

2020-06-12 04:11发布

My function creates a chain of generators:

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

My function sometimes needs to return chained in reversed order. Conceptually, the following is what I would like to be able to do:

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

Unfortunately:

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

What are my options?

This is in some realtime graphic rendering code so I don't want to make it too complicated/slow.

EDIT: When I first posed this question I hadn't thought about the reversibility of generators. As many have pointed out, generators can't be reversed.

I do in fact want to reverse the flattened contents of the chain; not just the order of the generators.

Based on the responses, there is no single call I can use to reverse an itertools.chain, so I think the only solution here is to use a list, at least for the reverse case, and perhaps for both.

7条回答
冷血范
2楼-- · 2020-06-12 05:00

reversed only works on objects that support len and indexing. You have to first generate all results of a generator before wrapping reversed around them.

However, you could easily do this:

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
查看更多
Evening l夕情丶
3楼-- · 2020-06-12 05:05
def reversed2(iter):
    return reversed(list(iter))
查看更多
一纸荒年 Trace。
4楼-- · 2020-06-12 05:05

In theory you can't because chained objects may even contain infinite sequences such as itertools.count(...).

You should try to reverse your generators/sequences or use reversed(iterable) for each sequence if applicable and then chain them together last-to-first. Of course this highly depends on your use case.

查看更多
你好瞎i
5楼-- · 2020-06-12 05:06
if num < 0:
    lst = list(chained)
    lst.reverse()
    return lst
else:
    return chained

reversed() needs an actual sequence, because it iterates it backwards by index, and that wouldn't work for a generator (which only has the notion of "next" item).

Since you will need to unroll the whole generator anyway for reversing, the most efficient way is to read it to a list and reverse the list in-place with the .reverse() method.

查看更多
我命由我不由天
6楼-- · 2020-06-12 05:06

itertools.chain would need to implement __reversed__() (this would be best) or __len__() and __getitem__()

Since it doesn't, and there's not even a way to access the internal sequences you'll need to expand the entire sequence to be able to reverse it.

reversed(list(CHAIN_INSTANCE))

It would be nice if chain would make __reversed__() available when all the sequences are reversable, but currently it does not do that. Perhaps you can write your own version of chain that does

查看更多
姐就是有狂的资本
7楼-- · 2020-06-12 05:15

You cannot reverse generators by definition. The interface of a generator is the iterator, which is a container that supports only forward iteration. When you want to reverse a iterator, you have to collect all it's items first and reverse them after that.

Use lists instead or generate the sequences backwards from the start.

查看更多
登录 后发表回答