반복자를 Python에서 재설정 할 수 있습니까?


답변:


84

itertools.tee 제안하는 많은 답변이 있지만 문서에서 중요한 경고 하나를 무시하고 있습니다.

이 itertool에는 상당한 보조 기억 장치가 필요할 수 있습니다 (저장해야하는 임시 데이터의 양에 따라 다름). 일반적으로 한 반복자가 다른 반복기가 시작되기 전에 대부분 또는 모든 데이터를 사용 list()하는 경우 tee().

기본적으로, tee서로 "동기 벗어나 려"동시에 두 개 (또는 그 이상)의 하나 반복자의 클론, 그렇게하지 않는 상황을 위해 설계되었습니다 많은에 의해 오히려 그들은 같은 "근처"에서 말하는 (A - 서로 뒤 또는 앞의 몇 가지 항목). OP의 "처음부터 다시 실행"문제에 적합하지 않습니다.

L = list(DictReader(...))반면에 딕셔너리 목록이 메모리에 편안하게 맞을 수있는 한 완벽하게 적합합니다. 새로운 "처음부터 반복자"(매우 가볍고 오버 헤드가 적음)는를 사용하여 언제든지 만들 수 있으며 iter(L)신규 또는 기존 항목에 영향을주지 않고 부분적으로 또는 전체적으로 사용할 수 있습니다. 다른 액세스 패턴도 쉽게 사용할 수 있습니다.

여러 답변을 바르게 언급으로, 특정의 경우에 csv할 수도 있습니다 .seek(0)기본 파일 오브젝트 (다소 특별한 경우). 나는 그것이 현재 작동하고 있지만 그것이 문서화되고 보장되는지 확실하지 않습니다. list일반적인 접근 방식이 너무 큰 메모리 풋 프린트를 가질 것이기 때문에 나는 정말 거대한 csv 파일에 대해서만 고려할 가치가있을 것입니다 .


6
list()5MB 파일의 csvreader를 통해 다중 통로를 캐시하는 데 사용하면 런타임이 ~ 12 초에서 ~ 0.5 초로 늘어납니다.
John Mee

33

'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를 만들 것을 제안했습니다. 첫 번째 독자가 파일을 읽는 중 절반 정도 인 경우 새 리더는 파일의 어디에 있든 예상치 못한 키와 값을 갖게되므로 작동하지 않습니다.


이것이 제 이론이 제게 말한 것입니다. 제가 생각했던 일이 일어나야한다는 것을 알게되어 반갑습니다.
Wayne Werner

@Wilduck : DictReader의 다른 인스턴스로 설명하는 동작은 새 파일 핸들을 만들고 두 번째 DictReader에 전달하면 발생하지 않습니다.

두 개의 파일 핸들러가 있으면 독립적으로 작동합니다.
Wilduck 2012 년

24

아니요. Python의 반복기 프로토콜은 매우 간단하며 하나의 단일 메서드 ( .next()또는 __next__()) 만 제공 하며 일반적으로 반복기를 재설정하는 메서드는 없습니다.

일반적인 패턴은 대신 동일한 절차를 다시 사용하여 새 반복자를 만드는 것입니다.

반복자를 "저장"하여 처음으로 돌아갈 수 있도록하려면 다음을 사용하여 반복자를 분기 할 수도 있습니다. itertools.tee


1
.next () 메서드를 분석하는 동안 아마도 정확할 수 있지만 작업이 요구하는 것을 얻는 매우 간단한 방법이 있습니다.
Wilduck 2010

2
@Wilduck : 나는 당신의 대답을 봅니다. 반복자 질문에 방금 대답했지만 csv모듈 에 대해 전혀 모릅니다 . 두 답변이 원본 포스터에 유용하기를 바랍니다.
u0b34a0f6ae

엄격히 말하면 반복자 프로토콜에는 __iter__. 즉, 이터레이터도 이터 러블이어야합니다.
Steve Jessop

11

, 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

다음 nditer과 같이 배열을 순환 할 수 있습니까 itertools.cycle?
LWZ

1
@LWZ : 나는 그렇게 생각하지 않습니다,하지만 당신은 할 수 과에 되는 예외을 . try:next()StopIterationreset()
추후 공지가있을 때까지 일시 중지되었습니다.


이것이 제가 찾던 것입니다!
sriram

1
여기서 "피연산자"의 제한은 32입니다. stackoverflow.com/questions/51856685/…
Simon

11

.seek(0)위의 Alex Martelli와 Wilduck이 옹호 한대로 사용하는 데 버그가 있습니다 . 즉,에 대한 다음 호출 .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)

5

이것은 원래 질문과 직교 할 수 있지만 반복기를 반환하는 함수로 반복자를 래핑 할 수 있습니다.

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)

이것은 티 (n 복사본) 또는 목록 (1 복사본)이 수행해야하는 캐싱을 피하는 것 같습니다.


3

작은 파일의 경우 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'}

여기서 a DictReaderseekable객체 (1)와 고급 (2)으로 래핑됩니다 . 이 seek()메서드는 반복기를 0 번째 위치 (3)로 재설정 / 되감기하는 데 사용됩니다.

참고 : 메모리 소비는 반복에 따라 증가하므로 문서에 표시된 대로이 도구를 대용량 파일에 적용하는 데주의 하십시오 .


2

반복자 재설정은 없지만 python 2.6 (이상)의 "itertools"모듈에는 도움이 될 수있는 몇 가지 유틸리티가 있습니다. 그 중 하나는 반복자의 여러 복사본을 만들고 앞서 실행중인 결과를 캐시하여 이러한 결과가 복사본에 사용되도록 할 수있는 "티"입니다. 나는 당신의 목적을 세울 것입니다 :

>>> 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]

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()

1

list(generator()) 생성기에 대한 나머지 모든 값을 반환하고 루프가 아닌 경우 효과적으로 재설정합니다.


1

문제

나는 전에 같은 문제가 있었다. 내 코드를 분석 한 후 루프 내에서 반복기를 재설정하려고하면 시간 복잡성이 약간 증가하고 코드가 약간 추악해진다는 것을 깨달았습니다.

해결책

파일을 열고 행을 메모리의 변수에 저장합니다.

# 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)

이제 반복자를 처리하지 않고도 범위의 어느 곳에서나 행을 반복 할 수 있습니다 .


1

한 가지 가능한 옵션 itertools.cycle()은를 사용 하는 것입니다. 이렇게하면 같은 트릭없이 무한 반복 할 수 있습니다 .seek(0).

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

1

나는이 같은 문제에 도달하고있다. 나는 tee()해결책이 마음에 들지만 내 파일이 얼마나 커질 지, 그리고 다른 하나가 그 방법을 채택하는 것을 미루기 전에 먼저 하나를 소비하는 것에 대한 메모리 경고를 모른다.

대신, iter()문을 사용하여 한 쌍의 반복자를 만들고 , 첫 번째 실행을 처음 실행에 사용하고, 마지막 실행을 위해 두 번째 실행으로 전환합니다.

따라서 dict-reader의 경우 리더가 다음을 사용하여 정의 된 경우 :

d = csv.DictReader(f, delimiter=",")

다음을 사용하여이 "사양"에서 반복기 쌍을 만들 수 있습니다.

d1, d2 = iter(d), iter(d)

그런 다음 d1두 번째 반복자 d2가 동일한 루트 사양에서 정의되었다는 사실을 알고 안전하게에 대해 첫 번째 패스 코드를 실행할 수 있습니다 .

나는 이것을 철저히 테스트하지는 않았지만 더미 데이터로 작동하는 것으로 보입니다.



0

'iter ()'호출 중 마지막 반복에서 새로 생성 된 반복기를 반환합니다.

class ResetIter: 
  def __init__(self, num):
    self.num = num
    self.i = -1

  def __iter__(self):
    if self.i == self.num-1: # here, return the new object
      return self.__class__(self.num) 
    return self

  def __next__(self):
    if self.i == self.num-1:
      raise StopIteration

    if self.i <= self.num-1:
      self.i += 1
      return self.i


reset_iter = ResetRange(10)
for i in reset_iter:
  print(i, end=' ')
print()

for i in reset_iter:
  print(i, end=' ')
print()

for i in reset_iter:
  print(i, end=' ')

산출:

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 5 6 7 8 9 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.