2011-02-09 12 views
37

は、このシナリオを考えてみましょう:Pythonジェネレータオブジェクトをクローンする方法は?

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import os 

walk = os.walk('/home') 

for root, dirs, files in walk: 
    for pathname in dirs+files: 
     print os.path.join(root, pathname) 

for root, dirs, files in walk: 
    for pathname in dirs+files: 
     print os.path.join(root, pathname)

私は、この例ではちょっと冗長ですが、我々は複数回同じwalkのデータを使用する必要があることを考慮すべきであることを知っています。私はベンチマークのシナリオをとっており、同じwalkデータの使用は、有益な結果を得るためには必須です。

私はwalk2 = walkを試して、2番目の反復でクローンして使用しましたが、機能しませんでした。問題は...私はそれをどのようにコピーできますか?それは今まで可能ですか?

ありがとうございます。

+0

'os.walk( '/ home')'を2回使用すると何が問題になりますか?どのように問題になるのですか? –

+2

@ S.Lottまあ、その種の仕事はそれぞれの実行で非常に異なります。別の問題は、最初の実行後にシステムがおそらく結果をキャッシュするため、次の実行では不正確な結果が得られることです。アイデアは、前に歩いて、それを引数として渡す2つのシナリオを測定することです。 :) –

+0

キャッシングは誤った結果を引き起こしません。 –

答えて

54

あなたはitertools.tee()を使用することができます。ドキュメントが指摘するように、これは、「重要な余分なストレージを必要とする」可能性があることを

walk, walk2 = itertools.tee(walk) 

注意。

+6

また、[documentation](http://docs.python.org/2/library/itertools.html#itertools.tee)は次のように述べています。「一般的に、あるイテレータが別のイテレータが開始する前にデータのほとんどまたはすべてを使用すると'tee()'の代わりに 'list()'を使う方が速いです。 " OPの元のコードスニペットが一度完全に反復され、再び 'list()'を使うことを勧められないでしょうか? – HorseloverFat

+0

[lambda:a_new_generator]のように、[ここ](http://stackoverflow.com/a/21315536​​/1959808)のように、キャッシュされたジェネレータを代わりに使用してください。 –

+1

[この回答]のコメントも参照してください(http://stackoverflow.com/a/1271481/1959808)。 –

4

両方をこのように使用されている

def walk_home(): 
    for r in os.walk('/home'): 
     yield r 

あるいはこの

def walk_home(): 
    return os.walk('/home') 

関数を定義:あなたはあなたがのために全体の発電機を反復処理しようとしているわかっている場合

for root, dirs, files in walk_home(): 
    for pathname in dirs+files: 
     print os.path.join(root, pathname) 
+1

OPが尋ねた正確な質問に対する答えではありませんが、これは完全なディレクトリツリーをメモリに保存しないと実行できません。 +1 –

+3

ループは不要です。 'def walk_home():return os.walk( '/ home')'は同じことをします。 – shang

+0

@Sven Marnach:「正確な」質問はほとんど意味がありません。 –

12

あらゆる使用法では、ジェネレータをリストに展開してリストを使用することで、おそらく最高のパフォーマンスが得られます 複数回。

walk = list(os.walk('/home'))

1

この答えは、他の回答を表明しているものに手の込んだ/伸ばすことを目指しています。ソリューションは必然的にあなたが達成しようとしているものが正確にに応じて異なります。

os.walkと同じ結果を何度も繰り返したい場合は、などのos.walkリストからリストを初期化する必要があります。

データが同じであることを保証する必要がある場合は、これがおそらく唯一のオプションです。しかしながら、これが不可能であるか望ましいことがいくつかのシナリオがあります。

  1. 出力は十分な大きさである場合は(すなわち、ファイルシステム全体list()にしようとすると、コンピュータがフリーズする)反復可能list()することはできません。
  2. それぞれの使用前に "新鮮な"データを取得したい場合は、反復可能であることは望ましくありません。

list()が適切でない場合は、必要に応じて発電機を起動する必要があります。発電機は、使用するたびに消火されるので、若干の問題があります。「再放送」あなたの発電機を複数回するためには、次のパターンを使用することができます。

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
import os 

class WalkMaker: 
    def __init__(self, path): 
     self.path = path 
    def __iter__(self): 
     for root, dirs, files in os.walk(self.path): 
      for pathname in dirs + files: 
       yield os.path.join(root, pathname) 

walk = WalkMaker('/home') 

for path in walk: 
    pass 

# do something... 

for path in walk: 
    pass 

前述のデザインパターンは、あなたのコードのDRYを維持することができます。

関連する問題