2011-02-15 48 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))