2011-10-26 11 views
3

のは、私はから10番目の要素を引くが、発電機は、次のようになり、私が書いた関数である最初の9を無視する発電機を持っているとしましょう今、私はそれを使用して、このようにそれのうち10番目のインデックスをつかむことができます。Pythonのジェネレータインデックスの最適化

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

これは一度だけexpensiveOperation 10回を実行した場合、または私は思ったんだけど? (編集:10回呼びますが、この次の部分は私が興味を持っています) expensiveOperationへの他の9回の呼び出しを破棄して最適化する方法はありますか?

私は、ジェネレータ関数を使用しないで、私が望むものを与えてくれるいくつかの他の解決策を考えることができますが、構文は単に反復関数をジェネレータに変えるだけのクリーンではありませんreturnyieldに置き換えてください。

編集: ジェネレータを安価に「スクロール」する方法を探しているように、必ずしもこの特定の問題を解決するつもりはありません。私が現在作業している実際のケースでは、myGeneratorを初めて呼び出すときに、私が実際にどのインデックスを必要としているかわからない。私は第15指数、次に27位、次に82位をつかむかもしれません。私はおそらく、最初の呼び出しで15位にスライスする方法を考え出すことができましたが、次に次回は12個以上スクロールする必要があります。

+0

'myGenerator'に' islice'機能を追加できますか? – 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"は低レベルのユーティリティファイルにあります。高価なコードに "expensiveOperation"を公開すると、私はむしろ保存したいカプセル化を破ります。しかし、私はラムダを返すことができます...前にラムダを生成することは考えていませんでした... –

+0

@Mike:あるいは、指定されたインターフェイスを持つカスタムプロキシクラスのインスタンスを返します。呼び出し可能。プロキシクラスのインターフェイスのみを公開します。 –

+0

lambdaを遅延演算に使用するのは、ラムダを作成するコストがexpensiveOperationのコストよりも小さい場合は、一般的なパターンです。 –

1

はのは、何が起こるか見てみましょう:あなたのコードがexpensiveOperationmyStateには影響を与えないことを示唆しているので、

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を切り離すことであろう。

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

次に、あなたがそれをしたいだけexpensiveOperationを適用します。

+0

はい、質問を投稿するとすぐ、私は実際にこれをテストするために出かけました。それは私のポスト修辞学の最初の "質問"を作りますが、2番目の質問は "私はそれを完全に書き直すことなく避けることができますか?" –

+0

@MikeEdwards:少なくともジェネレータを書き換える必要があります。 –

4

高価な操作をスキップすることができることを知っているのpythonのための方法はありません。たとえば、起こる必要のある副作用があるかもしれません。したがって、ジェネレータを早送りする方法はありません。

一つのオプション:

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

これは、呼び出し可能オブジェクトの代わりに、実際の値を返します。実際の値を取得するには、yieldされた値を呼び出します。その後、高価な操作が実行されます。

0

ジェネレータは、一度に1つのアイテムを消費することを意味します。それが作成するために多くの手間がかかりますが、あなたがあなたのケースで使用されなければならないことは反復可能である:

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__に多くのコードを追加する必要があります。