web-dev-qa-db-ja.com

Pythonでイテレータをリセットできますか?

Pythonでイテレータ/ジェネレータをリセットできますか?私はDictReaderを使用していますが、それを(csvモジュールから)ファイルの先頭にリセットしたいと思います。

107
user248237

itertools.tee を示唆する多くの回答がありますが、ドキュメントの重要な警告を無視しています:

このitertoolには、大量の補助ストレージが必要になる場合があります(一時データをどれだけ保存する必要があるかによって異なります)。一般に、あるイテレーターが別のイテレーターが開始する前にほとんどまたはすべてのデータを使用する場合、list()の代わりにtee()を使用する方が高速です。

基本的に、teeは、1つのイテレータの2つ(またはそれ以上)のクローンが、互いに「同期から外れる」間、そうしないように設計されています much-むしろ、彼らは同じ「近隣」で言います(お互いの背後または前方のいくつかのアイテム)。 OPの「最初からやり直す」問題には適していません。

一方、L = list(DictReader(...))は、辞書のリストがメモリに快適に収まる限り、完全に適しています。新しい「最初からのイテレータ」(非常に軽量で低オーバーヘッド)は、iter(L)を使用していつでも作成でき、新規または既存のイテレータに影響を与えることなく一部または全体で使用できます。他のアクセスパターンも簡単に利用できます。

いくつかの回答が正しく述べたように、csvの特定のケースでは、.seek(0)基になるファイルオブジェクト(かなり特殊なケース)を使用することもできます。それが文書化され保証されているかどうかはわかりませんが、現在は機能しています。一般的なアプローチではメモリフットプリントが大きすぎるため、listが推奨される、本当に巨大なcsvファイルについてのみ検討する価値があるでしょう。

70
Alex Martelli

「blah.csv」という名前のcsvファイルがある場合

_a,b,c,d
1,2,3,4
2,3,4,5
3,4,5,6
_

ファイルを読み取り用に開いて、DictReaderを作成できることを知っています。

_blah = open('blah.csv', 'r')
reader= csv.DictReader(blah)
_

その後、次の行をreader.next()で取得できます。

_{'a':1,'b':2,'c':3,'d':4}
_

もう一度使用すると

_{'a':2,'b':3,'c':4,'d':5}
_

ただし、この時点でblah.seek(0)を使用すると、次回reader.next()を呼び出すと、

_{'a':1,'b':2,'c':3,'d':4}
_

再び。

これはあなたが探している機能のようです。ただし、このアプローチに関連するいくつかのトリックがありますが、気づいていません。 @Brianは、単に別のDictReaderを作成することを提案しました。新しいリーダーは、ファイル内のどこにいても予期しないキーと値を持っているため、最初のリーダーがファイルを半分読んでいる場合、これは機能しません。

29
Wilduck

いいえ。Pythonのイテレータプロトコルは非常に単純であり、1つのメソッド(.next()または__next__())のみを提供し、一般的なイテレータをリセットするメソッドは提供しません。

一般的なパターンは、代わりに同じ手順を使用して新しいイテレータを作成することです。

イテレータを「保存」して先頭に戻るには、itertools.teeを使用してイテレータをフォークすることもできます

22
u0b34a0f6ae

上記のAlex MartelliとWilduckが提唱した.seek(0)の使用にはバグがあります。つまり、次の.next()の呼び出しは、ヘッダー行の辞書を{key1:key1、key2:key2の形式で提供します、...}。回避策は、file.seek(0)の後にreader.next()を呼び出してヘッダー行を削除することです。

したがって、コードは次のようになります。

f_in = open('myfile.csv','r')
reader = csv.DictReader(f_in)

for record in reader:
    if some_condition:
        # reset reader to first row of data on 2nd line of file
        f_in.seek(0)
        reader.next()
        continue
    do_something(record)
10

はいnumpy.nditerを使用してイテレーターを作成する場合。

>>> lst = [1,2,3,4,5]
>>> itr = numpy.nditer([lst])
>>> itr.next()
1
>>> itr.next()
2
>>> itr.finished
False
>>> itr.reset()
>>> itr.next()
1
10
Developer

これはおそらく元の質問と直交していますが、イテレータを返す関数でイテレータをラップすることができます。

def get_iter():
    return iterator

イテレータをリセットするには、関数を再度呼び出します。この関数が引数を取らない場合、これはもちろん簡単です。

関数がいくつかの引数を必要とする場合、functools.partialを使用して、元のイテレーターの代わりに渡すことができるクロージャーを作成します。

def get_iter(arg1, arg2):
   return iterator
from functools import partial
iter_clos = partial(get_iter, a1, a2)

これは、tee(nコピー)またはlist(1コピー)が必要とするキャッシュを回避するようです

4
Anish

イテレータのリセットはありませんが、python 2.6(以降)の「itertools」モジュールには、そこに役立つユーティリティがいくつかあります。その1つは、イテレータ、および実行中の結果をキャッシュして、これらの結果がコピーで使用されるようにします。

>>> def printiter(n):
...   for i in xrange(n):
...     print "iterating value %d" % i
...     yield i

>>> from itertools import tee
>>> a, b = tee(printiter(5), 2)
>>> list(a)
iterating value 0
iterating value 1
iterating value 2
iterating value 3
iterating value 4
[0, 1, 2, 3, 4]
>>> list(b)
[0, 1, 2, 3, 4]
2
jsbueno

小さなファイルの場合、 _more_itertools.seekable_ -イテラブルのリセットを提供するサードパーティ製ツールの使用を検討できます。

デモ

_import csv

import more_itertools as mit


filename = "data/iris.csv"
with open(filename, "r") as f:
    reader = csv.DictReader(f)
    iterable = mit.seekable(reader)                    # 1
    print(next(iterable))                              # 2
    print(next(iterable))
    print(next(iterable))

    print("\nReset iterable\n--------------")
    iterable.seek(0)                                   # 3
    print(next(iterable))
    print(next(iterable))
    print(next(iterable))
_

出力

_{'Sepal width': '3.5', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '5.1', 'Species': 'Iris-setosa'}
{'Sepal width': '3', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '4.9', 'Species': 'Iris-setosa'}
{'Sepal width': '3.2', 'Petal width': '0.2', 'Petal length': '1.3', 'Sepal length': '4.7', 'Species': 'Iris-setosa'}

Reset iterable
--------------
{'Sepal width': '3.5', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '5.1', 'Species': 'Iris-setosa'}
{'Sepal width': '3', 'Petal width': '0.2', 'Petal length': '1.4', 'Sepal length': '4.9', 'Species': 'Iris-setosa'}
{'Sepal width': '3.2', 'Petal width': '0.2', 'Petal length': '1.3', 'Sepal length': '4.7', 'Species': 'Iris-setosa'}
_

ここで、DictReaderseekableオブジェクト(1)および拡張(2)にラップされています。 seek()メソッドは、イテレータを0番目の位置にリセット/巻き戻すために使用されます(3)。

注:メモリの消費量は反復により増加するため、このツールを大きなファイルに適用する場合は注意してください ドキュメントで示されている

1
pylang

list(generator())は、ジェネレーターの残りの値をすべて返し、ループしていない場合は効果的にリセットします。

1
Theoremiser

問題

以前にも同じ問題がありました。コードを分析した後、ループ内でイテレータをリセットしようとすると、時間の複雑さがわずかに増加し、コードが少し見苦しくなります。

解決

ファイルを開き、行をメモリ内の変数に保存します。

# initialize list of rows
rows = []

# open the file and temporarily name it as 'my_file'
with open('myfile.csv', 'rb') as my_file:

    # set up the reader using the opened file
    myfilereader = csv.DictReader(my_file)

    # loop through each row of the reader
    for row in myfilereader:
        # add the row to the list of rows
        rows.append(row)

これで、イテレータを処理せずにスコープ内の任意の場所でrowsをループできます。

1

DictReaderの場合:

f = open(filename, "rb")
d = csv.DictReader(f, delimiter=",")

f.seek(0)
d.__init__(f, delimiter=",")

DictWriterの場合:

f = open(filename, "rb+")
d = csv.DictWriter(f, fieldnames=fields, delimiter=",")

f.seek(0)
f.truncate(0)
d.__init__(f, fieldnames=fields, delimiter=",")
d.writeheader()
f.flush()
0
mAsT3RpEE

基になる型がそうするためのメカニズムを提供する場合のみ(例:fp.seek(0))。

可能性のあるオプションは、 itertools.cycle() を使用することです。

iterDic = itertools.cycle(csv.DictReader(open('file.csv')))
0
Greg H