📌 이 포스팅에서는 Python의 반복을 가능하게 하는 iterator에 대해 정리하였습니다.
🔥 iterator란?
🔥 iterator 생성하기
🔥 이터레이터는 왜 whlie문 보다 for문과 자주 사용될까?
__iter__
) 속성이란?✔️ 이터레이터(iterator)는 값을 순회하여 꺼낼 수 있는 객체(object)를 의미하고, 이런 iterable한 객체들 str, list, dict, set 등이 있습니다.
✔️ 단, iterable한 객체 중 시퀀스 형태인 str, list, range, tuple은 요소의 다음 순서까지 가지고 있기 때문에 차례대로 꺼낼 수 있습니다. iterable형 중 순서까지 가지고 있는 것이 squence형인 것이죠. 순서가 없는 dict, set은 그냥 iterable형으로 볼 수 있습니다.
✔️ 반복문은 실행과 동시에 처음부터 끝까지 값을 만들어내며 메모리를 차지합니다.
for i in range(0,10): print(i, end=' ') # 0 1 2 3 4 5 6 7 8 9
✔️ 이터레이터는 값을 차례대로 꺼낼 수 있는 순회 가능한 객체를 생성한 상태로 대기했다가 값이 필요한 시점에서 순차적으로 next 매직 매서드를 통해 값을 만들 수 있기 때문에 메모리 효율성이 좋습니다.
✔️ 데이터 생성을 뒤로 미루는 이러한 방식을 지연 평가(lazyevaluation)라 합니다.
l = range(0, 10) l_iter = iter(l) # 이터레이터 생성 print(l_iter.__next__()) # 0 print(l_iter.__next__()) # 1 print(l_iter.__next__()) # 2 print(l_iter.__next__()) # 3
✔️ iterable한 객체를 iterator한 객체로 반환시키는 것이 iter 함수입니다. 이에 iterable한 객체가 iterator가 되기 전에는 next함수를 사용할 수 없습니다. 즉, iterable한 리스트나 딕셔너리가 for문에 들어가면 iterator로 사용할 수 있는 것이죠!
✔️ 우리는 이미 str, list, dict 등이 반복가능하단 사실을 알고 있습니다.
✔️ dir을 통해 객체 내부를 살펴보면,, 이는 __iter__
매직 매서드가 존재하는데 이 속성이 다음값을 순차적으로 지칭하는 역할을 맡고 있습니다.
print(dir(list())) """ ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index']
✔️ hasattr 함수는 확인하고자하는 속성이 존재하는지 검토해서 bool값을 반환해주기 때문에 dir로 탐색하는 것보다 간편합니다.
print(hasattr(int(), '__iter__')) # False print(hasattr(list(), '__iter__')) # True print(hasattr(dict(), '__iter__')) # True print(hasattr(str(), '__iter__')) # True print(hasattr(tuple(), '__iter__')) # True print(hasattr(set(), '__iter__')) # True
__iter__
매직 매서드로 이터레이터 생성하기✔️ tuple은 iter 매직 매서드가 존재하기 때문에 아래와 같이 이터레이터를 생성해 사용할 수 있습니다.
t = (1, 3, 9, 7, 5) print(hasattr(t, '__iter__')) # True iter_t = t.__iter__() print(iter_t) # <tuple_iterator object at 0x7fc0e6ee2cd0> 👈 이터레이터 생성 print(iter_t.__next__()) # 1 print(iter_t.__next__()) # 3 print(iter_t.__next__()) # 9
✔️ 0부터 10까지 정수를 랜덤하게 계속 생성하다 2가 뽑히면 빠져나가는 코드를 while문으로 구현하면 아래와 같습니다.
import random while_res = [] while True: i = random.randint(0,10) if i == 2: break else: while_res.append(i) print(while_res)
✔️ 이터레이터는 stop할 값을 지정해둘 수 있습니다. 아래처럼 이터레이터를 종료시킬 값을 지정하면 더욱 간결하게 코드 구현이 가능합니다.
import random iter_res = [i for i in iter(lambda : random.randint(0,10), 2)] print(iter_res)
✔️ next 매직 매서드를 통해 이터레이터의 값을 순차적으로 생성해 낼 때 더 이상 향할 값이 없으면 StopIteration 에러가 발생됩니다.
s = "python" l_iter = iter(s) print(l_iter.__next__()) # p print(l_iter.__next__()) # y print(l_iter.__next__()) # t print(l_iter.__next__()) # h print(l_iter.__next__()) # o print(l_iter.__next__()) # n print(l_iter.__next__()) # StopIteration 👈 에러 발생 print(l_iter) # <range_iterator object at 0x7f60ca4cbcf0>
✔️ 이를 예외처리를 통해 해결할 수 있지만, for문은 순회 가능한 범위까지만 next 매서드를 실행시킬 수 있기 때문에 훨씬 간편합니다.
✔️ while문으로 이터레이터를 제어한 코드는 아래와 같습니다.
D = {'a':1, 'b':2, 'c':3} d_iter = iter(D) while True: try: i = next(d_iter) except StopIteration: break print(i, end=" ") # a b c
✔️ for문으로 구현하면, StopIteration 손쉽게 제어할 수 있고, 더 간결합니다.
D = {'a':1, 'b':2, 'c':3} d_iter = iter(D) for i in range(len(D)): print(next(d_iter), end=' ') # a b c