# -*- coding: utf-8 -*-
"""
Created on Mon Jan  8 14:50:00 2018

@author: seung eon
"""

"""
이터레이터(iterator)는 값을 차례대로 꺼낼 수 있는 객체(object)입니다.
지금까지 for 반복문을 사용할 때 range를 사용했습니다. 만약 100번을 반복한다면
for i in range(100):처럼 만들었습니다. 이 for 반복문을 설명할 때
for i in range(100):은 0부터 99까지 연속한 숫자를 만들어낸다고 했는데,
사실은 숫자를 모두 만들어 내는 것이 아니라 0부터 99까지 값을 차례대로
꺼낼 수 있는 이터레이터를 하나만 만들어냅니다. 이후 반복할 때마다 이터레이터에서
숫자를 하나씩 꺼내서 반복합니다.

만약 연속한 숫자를 미리 만들면 숫자가 적을 때는 상관없지만 숫자가 아주 많을 때는
메모리를 많이 사용하므로 성능에도 불리합니다. 그래서 파이썬에서는 이터레이터만
생성하고 값이 필요한 시점이 되었을 때 값을 만드는 방식을 사용합니다. 즉, 데이터
생성을 뒤로 미루는 것인데 이런 방식을 지연 평가(lazy evaluation)라고 합니다.
참고로 이터레이터는 반복자라고 부르기도 합니다. 이 책에서는 이터레이터를 사용하겠습니다.
"""
it = [1, 2, 3].__iter__() # list는 반복 가능한 객체이다. __iter__로 이터레이터를 얻는다.
print('첫 번째',it.__next__())
print('두 번째',it.__next__())
print('세 번째',it.__next__())
#print('네 번째',it.__next__()) # 네번째 요소가 없기 때문에 StopIteration이 뜸

#리스트 뿐만아니라 'Hello, world!'의 문자열도 __iter__로 이터레이터 호출 가능
#또한 {'a':1, 'b':2}등의 딕셔너리와 {1, 2, 3}의 셋트도 씹가능!

it2 = range(3).__iter__()
print('2-첫 번째',it2.__next__())
print('2-두 번째',it2.__next__())
print('2-세 번째',it2.__next__())
print('2-네 번째',it2.__next__()) # 네번째 요소가 없기 때문에 StopIteration이 뜸

"""
이처럼 반복 가능한 객체는 __iter__ 메서드로 이터레이터를 얻고,
이터레이터의 __next__ 메서드로 반복합니다. 여기서는 반복 가능한 객체와 이터레이터가
분리되어 있지만 클래스에 __iter__와 __next__ 메서드를 모두 구현하면 이터레이터를
만들 수 있습니다. 특히 __iter__, __next__를 가진 객체를
이터레이터 프로토콜(iterator protocol)을 지원한다고 말합니다.
정리하자면 반복 가능한 객체는 요소를 한 번에 하나씩 가져올 수 있는 객체이고,
이터레이터는 __next__ 메서드를 사용해서 차례대로 값을 꺼낼 수 있는 객체입니다.
반복 가능한 객체와 이터레이터는 별개의 객체이므로 둘은 구분해야 합니다.
즉, 반복 가능한 객체에서 __iter__ 메서드로 이터레이터를 얻습니다.
"""