All Articles

Python - iterator

이터레이터(iterator)

이터레이터(iterator)는 값을 차례대로 꺼낼 수 있는 객체다. 반복 가능한(iterable) 객체에 __iter__() 메서드를 호출해서 이터레이터를 얻어내고, 그 이터레이터에 __next__() 메서드를 호출하면 요소를 차례대로 꺼낼 수 있다.

반복 가능한 객체가 시퀀스형 객체만을 의미하는 것이 아니다. 시퀀스형 객체는 반복 가능한 객체 중 순서가 정해진 객체만을 의미한다. 세트와 딕셔너리는 반복 가능한 객체지만 시퀀스형 객체는 아니다. 리스트, 튜플, 문자열, range는 시퀀스형 객체다.

반복 가능한(iterable) 객체

어떠한 객체가 반복 가능한 객체인지 확인하는 방법은 dir()함수로 호출해보는 것이다. dir() 내장 함수는 어떤 객체를 인자로 넣어주면 해당 객체가 어떤 변수와 메서드를 가지고 있는지 반환해준다. 출력값에 __iter__()가 있다면 반복 가능한 객체라는 의미다. 여기에 더해 __next__() 메서드까지 가지고 있다면 다음 요소를 하나씩 꺼내올 수 있는 반복 가능한 객체라는 의미다. (예 - 제너레이터)

L = [1, 2, 3]
print(dir(L))
# 출력값
['__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', 'insert', 'pop', 'remove', 'reverse', 'sort']

다만 주의할 점은 반복 가능한 객체가 곧 이터레이터를 의미하는 것은 아니라는 점이다. (iterable object ≠ iterator) 반복 가능한 객체를 __iter__() 메서드를 통해 이터레이터로 만들어주는 것이다.

이터레이터 요소 출력하기

위에서 언급한 리스트 L을 이터레이터로 만든 후 dir()함수로 다시 호출해보자.

x = L.__iter__()
print(dir(x))
# 출력값
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']

__iter__()에 이어 __next__()도 생긴 것을 볼 수 있다.

이제 리스트 L을 이터레이터로 만들어 변수 x에 저장했으니 요소를 하나씩 꺼내보자.

print(x.__next__())
print(x.__next__())
print(x.__next__())
print(x.__next__())
# 출력값
1
2
3
Traceback (most recent call last):
  File "hello.py", line 272, in <module>
    print(x.__next__())
StopIteration

리스트 안에 있던 요소 1, 2, 3이 모두 출력된 후 더이상 출력할 것이 없자 StopIteration이 발생했다. 이것을 이용하면 반복문에서 이터레이터가 반환할 요소가 없을 때 반복을 멈추도록 코드를 짤 수 있다.

while True:
  try:
    X = x.__next__()
  except StopIteration:
    break
  print(X ** 2)
# 출력값
1
4
9

숫자를 한 줄에 출력하고 싶으면

while True:
  try:
    X = x.__next__()
  except StopIteration:
    break
  print(X ** 2, end = " ")
1 2 3

참고로 iter()next()__iter___()__next__() 메서드를 사용하기 편하게 해주는 함수다.

L = [1, 2, 3]
x = iter(L)

while True:
  try:
    X = next(x)
  except StopIteration:
    break
  print(X ** 2, end = " ")

이렇게도 사용할 수 있다.


Q. 딕셔너리도 반복가능한 객체라서 앞서본 리스트와 같이 __iter__함수와 __next__함수를 사용할 수 있고 파이썬 기본함수인 iter, next 또한 사용할 수 있습니다. 다음의 간단한 키를 출력하는 딕셔너리에 대한 for 문을 while문으로 구현해 보세요.

D = {'a':1, 'b':2, 'c':3}
for key in D.keys():
    print(key)

A.

D = {'a':1, 'b':2, 'c':3}
d = iter(D)
i = 1
while i <= len(D):
    print(next(d))
    i += 1

또는

d = D.__iter__()
while True:
    try:
        x = d.__next__()
    except StopIteration:
        break
    print(x)
# 출력값
a
b
c