2016-09-23 19 views
0

私はcsvファイルからデータを抽出する2つの関数を持っています。リストを返し、もう1つはジェネレータを返します。ValueError:** generator **を使用した場合の閉じたファイルの入出力操作**

一覧:

def data_extraction(filename,start_line,node_num,span_start,span_end): 
    with open(filename, "r") as myfile: 
     file_= csv.reader(myfile, delimiter=',') #extracts data from .txt as lines 
     return [filter(lambda a: a != '', row[span_start:span_end]) \ 
     for row in itertools.islice(file_, start_line, node_num+1)] 

ジェネレータ:

def data_extraction(filename,start_line,node_num,span_start,span_end): 
    with open(filename, "r") as myfile: 
     file_= csv.reader(myfile, delimiter=',') #extracts data from .txt as lines    
     return (itertools.ifilter(lambda a: a != '', row[span_start:span_end]) \ 
       for row in itertools.islice(file_, start_line, node_num+1)) 

私はデータを抽出するために、以下のいずれかの関数への呼び出しで私のプログラムを起動します。 次の行は次のとおりです。print [x in data]

私は私が手発電機を使用するとき、それはすべて、正常に動作リストを返す関数を使用する場合:ValueError: I/O operation on closed file

を、私はそれが事実に起因したことを他の質問から収集しましたdata_extractionファンクションreturnsの場合、with openステートメントが失われている可能性があります。

質問は次のとおりです。私はすべてのコードを1つの関数の中に入れる必要がないように、データを抽出する独立した関数を持つことができる回避策がありますか?そして第二に、ジェネレータを複数回使用するようにリセットすることができますか?

ジェネレータをリストの上に保持したい理由は、大きなデータセットを扱っているからです。

+0

http://stackoverflow.com/questions/519633/lazy-method-for-reading-big-file-in-python –

+0

:あなたは今、このようにそれを使用することができます

class FileTrimmer(object): def __init__(self, filename, start_line, node_num, span_start, span_end): # store all attributes on self def __enter__(self): self._file = open(self.filename, "r") csv_reader = csv.reader(self._file, delimiter=',') #extracts data from .txt as lines return ( itertools.ifilter( lambda a: a != '', row[self.span_start:self.span_end]) for row in itertools.islice( csv_reader, self.start_line, self.node_num+1 )) def __exit__(self, *args, **kwargs): self._file.close() 

2番目の関数はジェネレータではありません.Ganaratorは何かを返すのではなく、 'yield'する必要があります。 –

+0

@ivan_pozdeev 2番目の関数*はジェネレータを返します。 – MisterMiyagi

答えて

1

withステートメントはファイルの最後を閉じます。つまり、これ以上データを読み取ることはできません。

リストの要素は実際にはすべてのデータを読み込みます。リスト要素は作成する必要があるためです。

ジェネレータのバージョンでは、実際にジェネレータからデータをフェッチするまで、データを読み取ることはありません。ファイルを閉じた後でそれを行うので、、次にがフェッチしようとするため、ジェネレータは失敗します。

これは、実際にデータを読み取ることによってのみ回避できます。あなたがリストを作成したのと同じように。すべてのデータ(ジェネレータ)を保持せずに、すべてのデータを保持したい(ファイルを閉じる)ことは理にかなっていません。

代わりに、ファイルを読み込むたびに開くことができます。ファイルオブジェクトはその値のジェネレータのように動作します。あなたは、フィルタリングコードの重複を避けたい場合は、このためにラッパーを作成することができます。これは、ビットを持って

def data_extraction(filename,start_line,node_num,span_start,span_end): 
    with open(filename, "r") as myfile: 
     file_= csv.reader(myfile, delimiter=',') #extracts data from .txt as lines    
     for item in (itertools.ifilter(lambda a: a != '', row[span_start:span_end]) \ 
      for row in itertools.islice(file_, start_line, node_num+1)): 
      yield item 

簡単な方法は、ジェネレータ関数自体にあなたのジェネレータを返す関数を有効にすることです問題が発生した場合:withステートメントは、ジェネレータが使い尽くされたり収集されたりすると閉じられます。これにより、開いているファイルを持っているのと同じ状況になります。

安全な代替は、フィルタ生成を持っており、それをファイルコンテンツを供給することである。

def data_extraction(file_iter, start_line, node_num, span_start, span_end): 
    file_= csv.reader(file_iter, delimiter=',') #extracts data from .txt as lines    
    for item in (itertools.ifilter(lambda a: a != '', row[span_start:span_end]) \ 
     for row in itertools.islice(file_, start_line, node_num+1)): 
     yield item 

# use it as such: 
with with open(filename, "r") as myfile: 
    for line in data_extraction(mayflies): 
     # do stuff 

あなたは、多くの場合、これを必要とする場合、あなたはまた、コンテキストマネージャプロトコルを実装することで、独自のクラスを作成することができます。これはopenの代わりにwithステートメントで使用できます。

with FileTrimmer('/my/file/location.csv', 3, 200, 5, 10) as csv_rows: 
    for row in csv_rows: # row is an *iterator* over the row 
     print('<', '>, <'.join(row), '>') 
+0

答えをありがとう! 'FileTrimmer'クラスの使用例を追加できますか?ファイルの内容を行ごとに印刷したいとします。 – Sorade

+1

@Sorade私は簡単な例を追加しました。入力ミスがある場合は私に教えてください。テストするためのCSVファイルがありません。 – MisterMiyagi

+0

私はFileTrimmerメソッドでは、__enter__ self._fileはopen(filename、 "r")ではなくopen(self.filename、 "r")でなければならないと思います。私がこの例を実行したとき、それは多くの行を出力しました: Sorade

関連する問題