TIL29. Python : 이터레이터(Iterator)의 이해

ID짱재·2021년 10월 10일
0

Python

목록 보기
35/39
post-thumbnail

📌 이 포스팅에서는 Python의 반복을 가능하게 하는 iterator에 대해 정리하였습니다.



🌈 이터레이터(Iterator)의 이해

🔥 iterator란?

🔥 iterator 생성하기

🔥 이터레이터는 왜 whlie문 보다 for문과 자주 사용될까?



1. iterators란?

🤔 이터레이터(__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


2. iterators 생성하기

🤔 __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

🤔 이터레이터의 stop 활용

✔️ 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)


3. 이터레이터는 왜 whlie문 보다 for문과 자주 사용될까?

✔️ 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
profile
Keep Going, Keep Coding!

0개의 댓글