# 이터레이터는 값을 차례대로 꺼낼 수 있는 객체이다.
# 여태 range를 이용한 for문을 많이 사용했는데,
# for i in range(n): 은 0부터 n - 1까지 숫자를 만드는 게 아니라
# 0부터 n - 1까지 값을 차례대로 꺼낼 수 있는 이터레이터를 하나만 만들어낸다.
# 숫자들을 미리 만들면 메모리를 많이 사용하게 되므로 성능에 불리하다.
# 그래서 파이썬에서는 이터레이터만 생성하고 값이 필요할 때만 값을 만드는 방식을 쓰는데,
# 이러한 방식을 지연 평가 라고 한다.
# 먼저 반복 가능한 객체에 대해 알아보자.
# 반복 가능한 객체는 문자열, 리스트, 딕셔너리, 세트를 말한다.
# 즉, 요소가 여러 개 있고 한 번에 하나씩 꺼낼 수 있는 개체를 말한다.
# 객체가 반복 가능한지 확인하는 방벙븐 객체에 __iter__ 메소드가 있는지 확인해보는 것인데,
# dir함수를 사용하면 객체의 메소드를 확인할 수 있다.
print(dir([1,2,3]))# [..., '__list__', ...] 와 같이 __iter__ 메소드가 들어있는 것을 확인할 수 있다.
it=[1,2,3].__iter__()# 이터레이터를 변수에 저장하고 __next__ 메소드를 호출해보면 요소를 차례대로 꺼낼 수 있다.
print(it)# <list_iterator object at 0x000002790A271CD0>
print(it.__next__())# 1
print(it.__next__())# 2
print(it.__next__())# 3
# print(it.__next__()) # 이렇게 한번 더 호출하면 StopIteration 예외가 발생한다.
# 이터레이터는 하나씩 꺼내오다가 꺼낼 요소가 없으면 StopIteration 예외를 발생시켜 반복을 끝낸다.
# __iter__와 __next__ 메소드를 클래스에 구현하면 이터레이터를 만들 수 있다.
# 이 두 메소드를 모두 가진 객체를 이터레이터 프로토콜을 지원한다고 말한다.
# 반복 가능한 객체: 요소를 한 번에 하나씩 가져올 수 있는 객체 ex) 리스트, 튜플, range, 문자열, 딕셔너리, 세트
# 이터레이터: __next__ 메소드를 사용해 차례대로 값을 꺼낼 수 있는 객체
# 시퀀스 객체: 반복 가능한 객체이면서 요소의 순서가 정해져 있고 연속적으로 이어져있는 객체 ex) 리스트, 튜플, range, 문자열
이터레이터 만들기
# 이번에는 __iter__, __next__ 메소드를 구현해서 직접 이터레이터를 만들어보자.
# class 이터레이터이름:
# def __iter__(self):
# 코드
#
# def __next__(self):
# 코드
classCounter:def__init__(self,stop):self.current=0# 현재 숫자 유지, 0부터 지정된 숫자 직전까지 반복을 끝낼 숫자
self.stop=stop# 반복을 끝낼 숫자
def__iter__(self):returnself# 현재 인스턴스를 반환
def__next__(self):ifself.current<self.stop:# 아직 반복을 끝내기 전일 때
cur=self.current# 반환할 숫자를 변수에 저장
self.current+=1# 현재 숫자를 1 증가
returncur# 숫자 반환
else:# 반복을 끝내야 할 때
raiseStopIteration# 예외 발생
foriinCounter(3):print(i,end=' ')# 0 1 2
print()# 이터레이터는 언패킹이 가능하므로, Counter 객체를 변수 여러 개에 할당할 수 있다.
# 이 때 이터레이터가 반복하는 횟수와 변수의 개수는 같아야 한다.
a,b,c=Counter(3)print(a,b,c)# 0 1 2
# 여태 써오던 map도 이터레이터라서 언패킹으로 변수 여러 개에 값을 할당할 수 있었다.
# 반환값을 저장할 때 _ 에 저장하는 경우가 있는데,
# 이건 반환값을 무시하겠다는 관례적 표현이다.
_,b=map(int,['1','2'])print(b)# 2
인덱스로 접근할 수 있는 이터레이터 만들기
# 지금까지는 __iter__ 과 __next__ 메소드를 구현해서 이터레이터를 만들었다.
# 이번엔 __getitem__ 메소드를 구현해 인덱스로 접근할 수 있는 이터레이터를 만들어보자.
# class 이터레이터이름:
# def __getitem__(self, index):
# 코드
classCounter:def__init__(self,stop):self.stop=stopdef__getitem__(self,index):ifindex<self.stop:returnindexelse:raiseIndexErrorprint(Counter(3)[0],Counter(3)[1],Counter(3)[2])# 0 1 2
foriinCounter(3):print(i,end=' ')# 0 1 2
print()# 이렇게 __init__ 메소드와 __getitem__ 메소드만 있는데도 동작이 잘 된다.
# 클래스에서 __getitem__ 메소드만 구현해도 이터레이터가 되며, __iter__, __next__ 메소드는 생략해도 된다.
iter, next 함수 활용하기
importrandom# 이번에는 파이썬 내장 함수 iter, next에 대해 알아보자
# iter은 객체의 __iter__메소드를 호출해주고, next는 객체의 __next__메소드를 호출해준다.
it=iter(range(3))print(next(it))# 0
print(next(it))# 1
print(next(it))# 2
# print(next(it)) # StopIteration 예외가 발생
# iter은 반복을 끝낼 값을 지정하면 특정 값이 나올 때 반복을 끝낸다.
# 이 때는 반복 가능한 객체 대신 호출 가능한 객체를 넣어준다.
# 참고로 반복을 끝낼 값은 sentinel(감시병)이라고 부른다
# iter(호출 가능한 객체, 반복을 끝낼 값)
it=iter(lambda:random.randint(0,5),2)# next(it)로 숫자를 만들다가 sentinel이 나오면 StopIteration 예외가 발생
# 2가 나올 때까지 계속 숫자를 만든다.
# 이렇게
foriinit:print(i,end=', ')# next는 기본값을 지정할 수 있다.
# 기본값을 지정하면 반복이 끝나더라도 StopIteration 예외를 발생시키지 않고 기본값을 반환한다.
it=iter(range(3))foriinrange(4):print(next(it,10))# 0 1 2 10
# 이터레이터를 만들 때 __iter__, __next__ 또는 __getitem__ 메소드를 구현해야 한다는 것을 기억하자.
Leave a comment