파이썬에서 반복자를 재설정 할 수 있습니까?
파이썬에서 반복자 / 생성기를 재설정 할 수 있습니까? DictReader를 사용하고 있으며 csv 모듈에서 파일의 시작 부분으로 재설정하고 싶습니다.
itertools.tee을 제안하는 많은 답변을 보았지만 문서에서 중요한 경고 하나를 무시하고 있습니다.
이 itertool에는 상당한 임시 데이터가 필요할 수 있습니다 (일시적으로 저장해야하는 임시 데이터 양에 따라 다름). 일반적으로 한 반복자가 다른 반복자가 시작하기 전에 대부분 또는 모든 데이터를 사용
list()
하는 경우 대신 대신 사용하는 것이 더 빠릅니다tee()
.
기본적으로, tee
하나의 이터레이터에 대한 두 개 이상의 복제본이 서로 "동기화"하는 동안 그렇게 많이 하지 않는 상황을 위해 설계되었습니다 . 서로 앞뒤에있는 몇 가지 항목). OP의 "처음부터 다시 실행"문제에 적합하지 않습니다.
L = list(DictReader(...))
반면에 dicts 목록이 메모리에 편안하게 들어갈 수있는 한 완벽하게 적합합니다. 새로운 "처음부터 반복자"(매우 가볍고 오버 헤드가 낮음)는 언제든 사용할 수 있으며 iter(L)
, 기존 또는 새로운 것에 영향을주지 않고 부분적으로 또는 전체적으로 사용될 수 있습니다. 다른 액세스 패턴도 쉽게 사용할 수 있습니다.
몇 가지 대답이 올바르게 언급되었으므로 특정 경우 기본 파일 객체 (특별한 경우) csv
를 사용할 수도 있습니다 .seek(0)
. 현재 작동하지만 문서화되고 보장되는지 확실하지 않습니다. 그것은 list
일반적으로 접근이 너무 큰 메모리 공간을 가지기 때문에 내가 추천 하는 진정으로 거대한 csv 파일에 대해서만 고려해 볼 가치 가 있습니다.
'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를 만들 것을 제안했습니다. 새 독자가 파일의 어느 위치에서나 예상치 못한 키와 값을 가지므로 처음 독자가 파일을 반쯤 읽는 경우에는 작동하지 않습니다.
파이썬의 반복자 프로토콜은 매우 단순하며, 단일 메소드 ( .next()
또는 __next__()
) 만 제공 하며 일반적으로 반복자를 재설정하는 방법은 없습니다.
일반적인 패턴은 동일한 절차를 다시 사용하여 새 반복자를 만드는 것입니다.
처음으로 되돌아 갈 수 있도록 반복자를 "저장"하려면 다음을 사용하여 반복자를 포크 할 수도 있습니다. itertools.tee
예 , 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
There's a bug in using .seek(0)
as advocated by Alex Martelli and Wilduck above, namely that the next call to .next()
will give you a dictionary of your header row in the form of {key1:key1, key2:key2, ...}
. The work around is to follow file.seek(0)
with a call to reader.next()
to get rid of the header row.
So your code would look something like this:
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)
This is perhaps orthogonal to the original question, but one could wrap the iterator in a function that returns the iterator.
def get_iter():
return iterator
To reset the iterator just call the function again. This is of course trivial if the function when the said function takes no arguments.
In the case that the function requires some arguments, use functools.partial to create a closure that can be passed instead of the original iterator.
def get_iter(arg1, arg2):
return iterator
from functools import partial
iter_clos = partial(get_iter, a1, a2)
This seems to avoid the caching that tee (n copies) or list (1 copy) would need to do
While there is no iterator reset, the "itertools" module from python 2.6 (and later) has some utilities that can help there. One of then is the "tee" which can make multiple copies of an iterator, and cache the results of the one running ahead, so that these results are used on the copies. I will seve your purposes:
>>> 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]
list(generator())
returns all remaining values for a generator and effectively resets it if it is not looped.
For small files, you may consider using more_itertools.seekable
- a third-party tool that offers resetting iterables.
Demo
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))
Output
{'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'}
Here a DictReader
is wrapped in a seekable
object (1) and advanced (2). The seek()
method is used to reset/rewind the iterator to the 0th position (3).
Note: memory consumption grows with iteration, so be wary applying this tool to large files, as indicated in the docs.
Problem
I've had the same issue before. After analyzing my code, I realized that attempting to reset the iterator inside of loops slightly increases the time complexity and it also makes the code a bit ugly.
Solution
Open the file and save the rows to a variable in memory.
# 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)
Now you can loop through rows anywhere in your scope without dealing with an iterator.
Only if the underlying type provides a mechanism for doing so (e.g. fp.seek(0)
).
For DictReader:
f = open(filename, "rb")
d = csv.DictReader(f, delimiter=",")
f.seek(0)
d.__init__(f, delimiter=",")
For 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()
One possible option is to use itertools.cycle()
, which will allow you to iterate indefinitely without any trick like .seek(0)
.
iterDic = itertools.cycle(csv.DictReader(open('file.csv')))
I'm arriving at this same issue - while I like the tee()
solution, I don't know how big my files are going to be and the memory warnings about consuming one first before the other are putting me off adopting that method.
Instead, I'm creating a pair of iterators using iter()
statements, and using the first for my initial run-through, before switching to the second one for the final run.
So, in the case of a dict-reader, if the reader is defined using:
d = csv.DictReader(f, delimiter=",")
I can create a pair of iterators from this "specification" - using:
d1, d2 = iter(d), iter(d)
I can then run my 1st-pass code against d1
, safe in the knowledge that the second iterator d2
has been defined from the same root specification.
I've not tested this exhaustively, but it appears to work with dummy data.
참고URL : https://stackoverflow.com/questions/3266180/can-iterators-be-reset-in-python
'programing tip' 카테고리의 다른 글
HTML5 캔버스에 SVG 파일 그리기 (0) | 2020.08.04 |
---|---|
Visual Studio에서 제로 참조 코드 목록 가져 오기 (0) | 2020.08.04 |
Mercurial에서 특정 버전의 파일을 얻는 방법은 무엇입니까? (0) | 2020.08.03 |
JavaScript로 길게 누르시겠습니까? (0) | 2020.08.03 |
PHP 현재 디렉토리 이름 가져 오기 (0) | 2020.08.03 |