39. 이터레이터(반복자)

Joy·2020년 4월 3일
0

이터레이터

이터레이터(iterator) :값을 차례대로 꺼낼 수 있는 객체(object)
for 반복문에서 0~99까지라고 하면 숫자 다 만드는게 아니고 차례로 꺼내는 이터레이터만 생성. 데이터 생성을 뒤로 미루는 것 - 지연 평가(lazy evaluation)

반복가능한 객체

반복 가능한 객체(iterable):요소가 여러 개 들어있고, 한 번에 하나씩 꺼낼 수 있는 객체 - 문자열, 리스트, 딕셔너리, 세트, range

iterable 확인하는 법: 객체의 메서드 중 iter 가 있나 확인 ( dir(객체) )

for 과 iterable

for에서 반복가능한 객체 사용할 때 과정

예)

반복 가능한 객체는 iter 메서드로 이터레이터를 얻고, 이터레이터의 next 메서드로 반복

반복 가능한 객체(iterable)와 이터레이터(iterator)는 별개의 객체이므로 둘은 구분:
반복 가능한 객체는 요소를 한 번에 하나씩 가져올 수 있는 객체이고, 이터레이터는 next 메서드를 사용해서 차례대로 값을 꺼낼 수 있는 객체. 즉, 반복 가능한 객체에서 iter 메서드로 이터레이터를 얻는다.

이터레이터 만들기

iter, next 메서드를 구현해서

class 이터레이터이름:
    def __iter__(self):
        코드
def __next__(self):
    코드


예)

``` python
class Counter:
    def __init__(self, stop):
        self.current = 0    # 현재 숫자 유지, 0부터 지정된 숫자 직전까지 반복
        self.stop = stop    # 반복을 끝낼 숫자
 
    def __iter__(self):
        return self         # 현재 인스턴스를 반환
 
    def __next__(self):
        if self.current < self.stop:    # 현재 숫자가 반복을 끝낼 숫자보다 작을 때
            r = self.current            # 반환할 숫자를 변수에 저장
            self.current += 1           # 현재 숫자를 1 증가시킴
            return r                    # 숫자를 반환
        else:                           # 현재 숫자가 반복을 끝낼 숫자보다 크거나 같을 때
            raise StopIteration         # 예외 발생
 
for i in Counter(3):
    print(i, end=' ')
실행 결과
  1. init 메서드 만들기
    Counter(3)처럼 반복을 끝낼 숫자를 받았으므로 self.stop에 stop을 넣어
    반복할 때마다 현재 숫자를 유지해야 하므로 속성 self.current에 0

  2. iter 메서드 만들기
    이 객체는 반복 가능한 객체이면서 이터레이터입

  3. next 메서드
    조건에 따라 숫자를 만들어내거나 StopIteration 예외를 발생

이터레이터 언패킹

결과를 변수 여러 개에 할당

반환값을 _에 저장

반환값을 언패킹했을 때 _에 할당하는 것은 특정 순서의 반환값 사용하지 않고 무시하겠다는 관례적 표현

>>> _, b = range(2)
>>> b
1

인텍스로 접근할 수 있는 이터레이터

getitem 메서드를 구현하여

class 이터레이터이름:
    def __getitem__(self, 인덱스):
        코드

클래스에서 getitem만 구현해도 이터레이터가 되며 iter, next는 생략해도 됩니다(초깃값이 없다면 init도 생략 가능).

예)

class Counter:
    def __init__(self, stop):
        self.stop = stop
 
    def __getitem__(self, index):
        if index < self.stop:
            return index
        else:
            raise IndexError
 
print(Counter(3)[0], Counter(3)[1], Counter(3)[2])
 
for i in Counter(3):
    print(i, end=' ')

getitem은 매개변수로 인덱스 index를 받음

iter, next 함수 활용

iter는 객체의 iter 메서드를 호출해주고, next는 객체의 next 메서드를 호출

iter는 반복 가능한 객체에서 이터레이터를 반환하고, next는 이터레이터에서 값을 차례대로 꺼냅

iter

iter는 반복을 끝낼 값을 지정하면 특정 값이 나올 때 반복을 끝냄 - 반복 가능한 객체 대신 호출 가능한 객체(callable)를 넣어줌. 반복을 끝낼 값은 sentinel

  • iter(호출가능한객체, 반복을끝낼값)

예) 0~5까지 랜덤으로 꺼내다가 2 나오면 반복끝내기

>>> import random
>>> it = iter(lambda : random.randint(0, 5), 2)
>>> next(it)
0
>>> next(it)
3
>>> next(it)
1
>>> next(it)
Traceback (most recent call last):
  File "<pyshell#37>", line 1, in <module>
    next(it)
StopIteration

next

기본값을 지정가능. -> 반복끝나도 예외 StopItertation 발생 x 기본값출력.

  • next(반복가능한객체, 기본값)

예)

>>> it = iter(range(3))
>>> next(it, 10)
0
>>> next(it, 10)
1
>>> next(it, 10)
2
>>> next(it, 10)
10
>>> next(it, 10)
10

문제

표준 입력으로 정수 세 개가 입력됩니다(첫 번째 정수는 시작하는 초, 두 번째 정수는 반복을 끝낼 초, 세 번째 정수는 인덱스이며 입력되는 초의 범위는 0~100000, 입력되는 인덱스의 범위는 0~10입니다). 다음 소스 코드에서 시간 값을 생성하는 이터레이터를 만드세요.

  • 시간 값은 문자열이고 시:분:초 형식입니다. 만약 숫자가 한 자리일 경우 앞에 0을 붙입니다(예: 12:01:08).
  • 1초는 00:00:01입니다. 23:59:59를 넘길 경우 00:00:00부터 다시 시작해야 합니다.
  • 시간은 반복을 끝낼 초 직전까지만 출력해야 합니다(반복을 끝낼 초는 포함되지 않음).
class TimeIterator:
    def __init__(self, start, stop):
        self.start = start
        self.stop = stop
        self.current = 0

    def __getitem__(self, index):
        if index < self.stop - self.start:
            h = (self.start+index)//60//60%24
            m = (self.start+index)//60%60
            s = (self.start+index)%60
            return '{0:02d}:{1:02d}:{2:02d}'.format(h, m, s)
        else:
            raise IndexError
            
            
start, stop, index = map(int, input().split())
 
for i in TimeIterator(start, stop):
    print(i)
 
print('\n', TimeIterator(start, stop)[index], sep='')
  1. 클래스 만들어서 이터레이터 생성 - init
  2. 인덱스 처리하는 getitem 생성
    인덱스는 끝이랑 시작값 뺀거보다 작아야함. 작으면 시간 리턴하는 식 쓰기. 시간은 포멧으로 형식 지정.
    
profile
roundy

0개의 댓글