# * 이터레이터의 특별한 특징

Written by 📝 Hidekuma

# 소진성

제너레이터가 반환한 이터레이터는 호출 되면, StopItreration Exception을 일으킨 후 데이터가 소진된다.

# 예시1

제너레이터를 생성하는 함수이며, 리턴받은 이터레이터를 리스트로 만든다(순회시킨다).

def generated_data():
    for i in range(10):
        yield i
result = generated_data()

print(result) # 제너레이터 생성확인
print(list(result)) # 제너레이터 첫번째 순회 - 이터레이터 순회 (StopIteation)

>>>
<generator object generated_data at 0x102402be0>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1
2
3
4
5
6
7
8
9
10
11

여기서 한번 더 호출해본다.

print(list(result)) # 제너레이터의 두번째 순회 - 이터레이터 이미 소진

>>>
[] # 빈 list
1
2
3
4

제너레이터의 반환 값에 아무런 결과도 생성되지 않는다. 이런 특징을 일회성(소진성)이라고 한다. 이러한 소진성을 해결하기 위해서는 제너레이터에서 반환한 이터레이터를 새로운 변수에 할당하여, 복사해야한다.

# 예시2

예시1과 같은 코드이나 결과값을 복사하였다.

def generated_data():
    for i in range(10):
        yield i
result = generated_data()

result_copy = list(result)
print(result_copy)
print(result_copy)

>>>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1
2
3
4
5
6
7
8
9
10
11
12

코드가 잘 동작하지만, 이렇게 하는 방법은 처음부터 리스트를 만드는 것과 퍼포먼스 적으로 큰 기대를 할 수 없다. 특히, 입력받은 데이터가 크다면 더욱이 문제이다.

# 이터레이터 프로토콜

Iterator protocolfor문과 같은 파이썬 표현식이 콘텐츠(컨테이너 타입)를 탐색방법이다.

예를 들어, 파이썬이 for문을 해석할 때, 결과적으로는 입력값의 __iter__를 호출한다. 좀 더 자세한 흐름은 다음과 같다.

  1. for x in x_list 해석시작
  2. iter(x_list) 내장함수 실행
  3. x_list.__iter__ 호출
  4. __iter__는 이터레이터 객체를 반환

따라서 for문의 입력값에 이터레이터가 아닌 데이터 타입을 넣게 되면, 우리는 종종 다음과 같은 에러를 마주하게 된다.

TypeError: 'int' object is not iterable
1

for 문의 원리

즉, 우리가 자주 사용하는 for 문의 원리는 __iter__에게서 반환된 이터레이터가 StopItreration Exception이 일어날때 까지 이터레이터의 next()를 계속 호출한다.

# 이터러블(iterable:순회기능) 구현

예시2를 단점을 해결하고자 기능을 다음과 같이 컨테이너 타입으로 구현한다.

class generator_data(object):
    def __init__(self, length):
        self.length = length
    def __iter__(self):
        for i in range(self.length):
            yield i

gen_data = generator_data(10)

print(gen_data)
print(list(gen_data)) # 첫번째 호출 - __iter__가 호출되어 새로운 이터레이터 반환
print(list(gen_data)) # 두번째 호출 - 상기 동일
>>>

<__main__.generator_data object at 0x10c973c50>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

해당 코드로 우리가 원하는 결과를 얻을 수 있었다. 코드가 정상적으로 동작하는 이유는, 각각의 순회 과정에서 이터레이터는 독립적으로 동작(새로운 이터레이터 객체가 반환)하기 때문이다.

컨테이너 타입과 이터레이터 타입

  • iter내장 함수에 이터레이터를 넘기면 이터레이터 자체를 반환
    • 소진된 이터레이터가 들어오면 소진된 채로 반환
  • iter내장 함수에 컨테이너 타입을 넘기면 매번 새로운 이터레이터를 생성해서 반환
Last Updated: 7/5/2019, 8:38:55 AM