Python: Iterable, and Iterator

** 원 글은 이글루스. 살짝 고치고, 조금 덧붙였다.


파이썬을 공부하다가 첫번째로 부딪히게 되는 관문이 바로 이것이 아닐까..
다른 이들은 어떤지 모르겠지만, 적어도 나는 여기서 한동안 전진을 하지 못했었다.
꼭 이걸 알아야 다음으로 넘어갈 수 있는 건 아닌데..

뭐든 공부라는 것이 한단계 한단계 밟아야 하는 건 맞지만, 꼭 그 단계별로 완벽하게 이해를 해야하는 건 아닌 것도 같다.
당시에는 모르고 대충 듣고만 넘어가도, 계속 공부하다 보면, 언젠가 자연스레 알게되는 때가 오게 되는 법.
그 ‘때’가 얼마나 빨리 오느냐, 늦게 오느냐, 아예 오지 않느냐는 노력과 재능에 달린 것이겠지만..

자…
이제 Iterable, Iterator 에 대해 알아보자.
이 블로그에 쓰인 모든 글이 그렇지만, 모든 내용은 내가 임의로(?) 쓴 것이다. 절대적인 사실은 아니라는 것.

먼저 Iterable.
파이썬에는 List 라는 막강한 객체가 있고, 이 객체를 존재하게 해주는 강력한 힘은 Iteration Protocol 이다.
예를 들어서, 아래와 같은 List Comprehension 을 보자.

A = [ x ** 2 for x in [2,3,5,7]]

위 L.C 는, 리스트인 [2,3,5,7] 의 요소 하나 하나를 끄집어 내어 제곱한 뒤, A 라는 변수에 리스트 형태로 넣는 명령이다.
명령은 간단하지만, 내가 인식하지 못하는 사이에, 파이썬은 뒤에서 뭔가 ‘은밀한’ 작업을 한다.
그 첫번째는, 리스트인 [2,3,5,7]을 이터레이터 객체로 변환한 뒤, next() 함수 (또는 next() 메써드) 를 사용하여, 이터레이터 내의 인자를 하나씩 반환하게 만드는 작업이다. 이 작업은 StopIteration 이 발생될 때까지 계속된다.
(위에서 리스트를 이터레이터 객체로 변환했다라는 표현을 썼다. 이것을 기억하고 있어야 한다. 설명은 아래에 나온다.)

Iterate 의 원 뜻은 ‘(컴퓨터에서 계산 처리절차를) 반복하다’ 라고 돼 있다.

그렇다면!
Iterable 은, 반복가능한 객체를 일컬음이고, 이걸 좀 달리 표현해보면, 반복이라기 보다는 왼쪽부터 시작하여 오른쪽으로 진행하며 하나씩 요소를 넘겨줄 수 있는 객체를 말한다. List, Set, Dictionary, Tuple 등등이 Iterable 한 객체가 되겠다.

Iterator는??
반복가능한 객체는 그럼 모두 Iterator 인가? 파이썬의 대표주자인 List 는 Iterator 인가?
헷갈리게도, List 는 Iterator 가 아니다. Itearble 이지만, Iterator 객체는 아니다.

참고로, iterator 인지 확인하려면 collections.abc.Iterator 를 써야 한다. 이전에는 collections.Iterator 였는데, 최근 판에선 이렇게 바뀌었다.

import collections
isinstance(obj, collections.abc.Iterator)

그렇다면, Iterator 의 정의는 뭔가??
➙ Iterable 한 객체의 요소(값)를 순서대로 접근할 수 있게 해주는 객체. (1회만 진행되고 파기됨)
next() 메쏘드 사용가능한 객체.

➙ for loop 시 iterable 객체를 순환시키기 위해서는 Iterator 로 변환해야만 한다.

하여, 위에서 만든 리스트 A 에 next 를 적용하면 오류가 발생한다. 리스트는 1회만 Iterate 되고 파기되지 않기 때문이다.
Iterator 로 보이는 range 객체도 Iterable 이나 Iterator 는 아니다.

next(A)
Traceback (most recent call last):
File "", line 1, in
next(A)
TypeError: list object is not an iterator

foo = range(1,5)
next(foo)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-4e14a8ea1ec1> in <module>
----> 1 next(foo)

TypeError: 'range' object is not an iterator

# 또는 이런 예도 가능하다.
aa = range(0,5)
for i in aa:
    print(i)
0
1
2
3
4
# 한번 더 돌려도 된다.
for i in aa:
    print(i)
0
1
2
3
4

# 그러나, iterator 로 바꾸고 나면,
bb = iter(range(0,5))
for i in bb:
    print(i)
0
1
2
3
4

# 한번 더 돌리면 아무 반응이 없다. Iterator 는 한번 순환되면 수명이 끝나기 때문.
for i in bb:
    print(i)

Iterable 이나 Iterator 는 아니다.. 그럼, Iterable 한 객체를 Iterator 로 만들 수는 있는 건가?
그럴 때 쓰라고 Iter 함수가 존재한다.

B = iter(A)
next(B) ---> 4

위와 같은 결과를 보여준다.

next 함수 (또는 next() 메써드) 의 정확한 역할은,
Iterator 가 가리키는 첫번째 요소(값)를 Return 한 후, 다음 요소를 가리키게 함. 요소가 모두 없어졌을 때는 StopIteration 예외를 발생.

쉽게 생각하여, Iterable 은 다른 언어에서 ‘배열’이라 불리는 파이썬의 모든 객체(List, Set, 등등등)을 말한다고 보면 되겠다.

이에 대해 아주 자세하게 설명한 글을 찾았다. 이 글의 내용을 모두 이해하면, Python 으로 한걸음 더 다가섰다고 할 수 있겠다.
아무튼, 이 글을 참고하면 for loop 는 이런 식으로 작동한다.

def funky_for_loop(iterable, action_to_do):
    iterator = iter(iterable)
    done_looping = False
    while not done_looping:
        try:
            item = next(iterator)
        except StopIteration:
            done_looping = True
        else:
            action_to_do(item)

Iterable 객체를 for 에서 돌리려면 일단 Iterator 로 바꾼 뒤 while loop 에 넣고 next() 로 Iterator 를 계속 순환시킨다.

언제까지?
StopIteration 이 발생할 때까지.
StopIteration 은 누가 일으켜?
next() 함수가.

Author: 아무도안

안녕하세요. 글 남겨주셔서 고맙습니다.