2011-10-26 58 views
3

比方說,我有一臺發電機,我想從拉動10元,但忽略前9發電機是我寫了一個函數,看起來是這樣的:Python的發電機索引優化

def myGenerator(arg1, arg2): 
    for i in arg1: 
     myState = doSomeWork(i, arg2) 
     yield expensiveOperation(myState) 

現在我可以使用它,並抓住10指數,它像這樣:

myGen = myGenerator(list1, list2) 
tenthElement = next(itertools.islice(myGen,10,11)) 

我想知道如果這個運行expensiveOperation十倍,或者只是一次? (編輯:它調用它的10倍,但是這下一部分是我很感興趣)。 有沒有什麼辦法,因爲他們被丟棄優化掉其他9調用expensiveOperation? (編輯爲清楚起見)

我能想到的,不涉及使用發電機功能,並會正是我想要給其他幾個解決方案,但語法不是那樣乾淨只是把一個迭代函數到發電機通過用yield替換return

編輯: 我不一定試圖解決這個特定的問題,而是尋找一種廉價「滾動」發電機的方法。在我目前有工作的實際情況,我真的不知道我想要的指數當我打電話myGenerator首次。我可以抓住第15個指數,然後是第27個指數,然後是第82個指數。我大概能想出辦法切片15日在第一次通話,但後來我需要在下一次滾動12個左右。

+0

您可以將'islice'功能添加到'myGenerator'嗎? – eumiro

回答

5

發電機從其消費中分離 - 它不知道什麼被扔掉。所以,是的,它在每一步都進行昂貴的操作。

我只想移動昂貴的操作發電機外:

def myGenerator(arg1, arg2): 
    for i in arg1: 
     myState = doSomeWork(i, arg2) 
     yield myState 

myGen = myGenerator(list1, list2) 
tenthElement = expensiveOperation(next(itertools.islice(myGen,10,11))) 
+0

「myGenerator」處於低級別實用程序文件中。向高級代碼暴露「昂貴的操作」會破壞我寧願保留的封裝。但我可以返回一個lambda ...我從來沒有想過一個lambda生成生成器之前...... –

+0

@Mike:或者,返回自定義代理類的實例與指定的接口,如果你需要更復雜的東西比返回一個調用。你只會暴露你的代理類的接口。 –

+0

只要創建lambda的成本低於expensiveOperation的成本,對延期操作使用lambda就是一個很好的通用模式。 –

1

讓我們看看會發生什麼:

def expensive_operation(x): 
    print 'expensive operation', x 
    return x 

def myGenerator(): 
    for i in xrange(1000): 
     yield expensive_operation(i) 

myGen = myGenerator() 
tenthElement = next(itertools.islice(myGen,10,11)) 
print 'tenthElement', tenthElement 

打印

expensive operation 0 
expensive operation 1 
expensive operation 2 
expensive operation 3 
expensive operation 4 
expensive operation 5 
expensive operation 6 
expensive operation 7 
expensive operation 8 
expensive operation 9 
expensive operation 10 
tenthElement 10 

最好是從myGenerator分離expensiveOperation因爲你的代碼表明,expensiveOperation不影響myState

def myGenerator(arg1, arg2): 
    for i in arg1: 
     myState = doSomeWork(i, arg2) 
     yield myState 

然後只在需要時應用expensiveOperation

+0

是的,只要我發佈這個問題,我就會像這樣去測試它。這讓我在文章中出現了第一個「問題」,但是第二個問題「我能否避免全部重寫」是我真正的追求。 –

+0

@MikeEdwards:至少需要重寫發生器。 –

4

有沒有辦法讓蟒蛇知道,昂貴的操作可以跳過。例如,它可能有需要發生的副作用。所以沒有辦法快速發送發電機。

一個選項:

def myGenerator(arg1, arg2): 
    for i in arg1: 
     myState = doSomeWork(i, arg2) 
     yield functools.partial(expensiveOperation, myState) 

這將返回一個可調用對象,而不是實際值。要獲得實際值,請調用yield值。只有這樣才能執行昂貴的操作。

0

發電機是指在同一時間被消耗一個項目。雖然它需要更多的創建工作,你應該用你的情況有什麼可迭代:

class myIterable(): 
    def __init__(self, arg1, arg2): 
     self.arg1 = arg1 
     self.arg2 = arg2 
    def __getitem__(self, index): 
     myState = doSomeWork(self.arg1[index], self.arg2) 
     return expensiveOperation(myState) 

myIter = myIterable(list1, list2) 
tenthElement = myIter[10] 

你需要更多的代碼添加到__getitem__,如果你想支持片和負索引。