39. 이터레이터

이터레이터?

  • 이터레이터는 값을 차례대로 꺼낼 수 있는 객체이다.

다음과 같은 코드를 실행한다고 하자.

for i in range(100)

이 코드는 0~99까지의 숫자를 모두 만들어내는 것이 아니라, 0~99까지의 값을 차례대로 꺼낼 수 있는 이터레이터를 하나 만들어내는것!
코드가 반복될때마다 만들어진 이터레이터에서 숫자를 꺼내서 반복하는 것이다.

반복 가능한 객체

  • 우리가 흔히 사용하는, 문자열, 리스트, 딕셔너리 등등이 반복 가능한 객체
  • 요소가 여러개 들어있고, 한 번에 하나씩 꺼낼 수 있는 객체를 말한다.
  • 객체에 iter메소드가 있다면 반복 가능한 객체이다.

반복 가능한 객체인 [1,2,3]의 이터레이터를 확인해보자.

[1,2,3].__iter__() 
>> list_iterator object...

이터레이터를 변수에 저장하고, next메소드를 호출해보자.

it = [1,2,3].__iter__()

>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
3
>>> it.__next__()
StopIteration

이처럼, next메소드를 호출하면 순서대로 1,2,3이 나오고 3이 나온 후 next메소드를 출력하면 StopIteration이 발생해 반복이 끝나게 된다.

for와 반복 가능한 객체

for에 반복 가능한 객체인 range을 사용했을때 동작 과정은 위 사진과 같다.
먼저 range에서 iter를 얻은 후, next메소드를 사용해 숫자를 차례대로 하나씩 꺼내 i에 저장하고, 마지막 숫자가 될때 stopiteration이 발생해 반복이 끝나게 된다.

정리해보면, 우리는 반복가능한 객체에서 iter메소드로 이터레이터를 얻는다. 여기서 이터레이터는 next메소드를 사용해 순서대로 값을 꺼낼 수 있는 객체를 말한다.

이터레이터 만들기

이제 우리가 직접 iter, next메소드를 구현해 이터레이터를 만들어보자.

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=' ')
    # 0 1 2

우리는 위 코드에서 Counter이라는 이터레이터를 만들었다.
counter는 0부터 지정 숫자직전까지 반복하기 때문에 self.current에 0을 넣고, self.stop에 끝내고 싶은 숫자를 넣어준다.

iter 메소드에서는 self만 반환한다.
이 객체는 리스트, 문자열, 딕셔너리, 세트, range처럼 iter를 호출해줄 반복 가능한 객체가 없으므로 현재 인스턴스를 반환하면 된다.

next메소드에서는 조건에 따라 숫자를 만들어내거나 stopiteration을 발생시킨다.
현재 숫자 self.current가 반복을 끝낼 숫자 self.stop보다 작을 때는 self.current를 1 증가시키고 현재 숫자를 반환한다.
self.current가 self.stop보다 크거나 같아질 때는 raise StopIteration으로 예외를 발생시키게 된다.

이터레이터 언패킹

이터레이터로 발생한 결과를 변수 여러개로 할당 할 수 있다.
예를들어, 아까 만들어본 이터레이터의 결과를 변수 여러개로 할당하는 코드는 다음과 같다.

a, b, c = Counter(3)
print(a, b, c)
0 1 2

인덱스로 접근할 수 있는 이터레이터 만들기

우리는 getitem 메소드를 이용해 인덱스로 접근할 수 있는 이터레이터를 만들 수 있다.

위에서 만든 Counter 이터레이트를 getitem을 이용하여 다시 만들어보자.

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])  
# 0 1 2
for i in Counter(3):
    print(i, end=' ') # 0 1 2

이번 클래스에서는 getitem 메소드만으로 구현하였다.

getitem 메소드에서는 index를 받고, index가 지정된 숫자보다 작을때 index를 반환한다.
만약, index가 지정된 숫자보다 크거나 같으면 IndexError 예외를 발생시킨다.

iter와 next함수 활용하기

iter은 반복을 끝낼 값을 지정하면 특정 값이 나올 때 반복을 끝낸다.

import random
for i in iter(lambda : random.randint(0, 5), 2):
     print(i, end=' ')

3 1 4 0 5 3 3 5 0 4 1 

위 코드처럼, iter 함수를 사용하면, if 조건문을 지정해주지 않아도 간단하게 반복문을 종료할 수 있다.

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

위 코드는 기본값을 10으로 지정하여 반복이 끝나도 10이 나오게 된다.

좋은 웹페이지 즐겨찾기