Iterator(반복자)를 먼저 알아보기 전에 Iterable이 무엇인지 알아야 하겠죠?

먼저 Python doc의 Iterable 정의를 보도록 합시다.



 Iterable


  An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as liststr, and tuple) and some non-sequence types like dict and file and objects of any classes you define with an __iter__() or __getitem__() method. Iterables can be used in a for loop and in many other places where a sequence is needed (zip()map(), ...). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also iterator, sequence, and generator.



iterable을 정리해보면 member를 하나씩 반환할 수 있는 object를 말합니다.

iterable의 예로는 sequence type인 list, str, tuple이 있습니다. 


데이터 타입에 상관 없이 순회를 구현하고자 하는 필요성은 파이썬에만 있었던 것은 아니었다고 합니다. Iterator Pattern은 이미 하나의 디자인 패턴으로 정형화 되어 있는 것 중 하나입니다. 데이터 타입의 내부 구현을 노출하지 않고 포함하고 있는 요소들을 순회할 수 있는 방법을 제공하는 패턴을 이터레이터 패턴이라고 합니다.

※ 대부분 언어에서 이터레이터 패턴을 콜렉션 라이브러리의 구현 방식이나 문법 요소로 제공하고 있기는 하지만 파이썬 만큼 언어 자체에 자연스럽게 녹아 있는 경우는 많지 않다록 합니다.


사실 단순한 for문의 range도 iterable할 수 있는 list가 생성되었기 때문에 차례대로 사용할 수 있었던 것입니다.

for i in range(0,3):
	print(i)
#0
#1
#2

즉, range로 생성된 list가 iterable 할 수 있기 때문에 차례대로 member를 불러와 쓸 수 있다는 것입니다.

그리고 dictionary의 type인 dict도 iterable 할 수 있습니다. 그래서 다음과 같은 결과를 가지고 옵니다.

x = ['a':1, 'b':2, 'c':3]

for i in x:
	print(i)
	
#a
#b
#c

그리고 __iter__()나 __getitem__() 메소드로 정의된 class는 모두 iterable 할 수 있다고 합니다. 

iterable은 for loop말고도 zip(), map()과 같이 sequence 한 특징을 필요로 하는 작업을 하는 데 유용합니다.



zip([iterable, ...])

  This function returns a list of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.


map(function, iterable, ...)

  Apply function to every item of iterable and return a list of the results.



zip()과 map()을 보면 argument를 iterable로 받는 것을 알 수 있습니다.


그리고 이제 iterator를 보도록 합시다.

iterator를 보기 앞서 먼저 Python docs의 정의를 살펴보도록 하지요.



Iterator


  An object representing a stream of data. Repeated calls to the iterator’s next() method return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its next() method just raise StopIteration again. Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.


 

iterator는 next()함수로 데이터를 순차적으로 호출 가능한 object입니다. 만약 마지막 데이터를 불러오고도 next()를 하거나 next()함수로 데이터를 가져올 수 없는 경우는 StopIteration exception을 발생시킵니다.


그렇다면 Iterable한 object는 iterator일까요?

결론부터 말씀드리자면, iterable하다고 해서 무조건 iterator라고 할 수는 업습니다.


저는 처음 iterator를 공부할 때, list를 iter()함수를 통해 iterable하게 만들어서 object를 생성해보았었습니다.

list는 iterable이지만 next()함수로는 순차적으로 불러올 수 없었습니다. list로 선언된 것은 보통 a[0], a[1] ...등으로 불러오지요.

list는 순회 가능한(iterable) 오브젝트이지만 __getitem__() 함수를 IndexError가 발생할 때까지 0부터 차례로 호출하기 때문에 개념이 약간 다르다고 할 수 있습니다.



Iterator의 자세한 원리


iter() 함수는 들어온 컨테이너의 __iter__ 함수를 호출해주며, __iter__는 해당 컨테이너의 이터레이터를 반환(return)하도록 구현되어 있습니다. (이렇게 반환된 이터레이터는 __iter__ 함수와 next() -->(python3 에서는 __next__) 함수를 구현하고 있어야 함, 역으로 두 함수를 구현하고 있는 오브젝트를 이터레이터라고 함)
이터레이터의 __iter__와 next(혹은 __next__)는 내부적으로 사용하도록 의도된 함수이기 때문에 직접 부르기보다는 각각 내장 함수인 iter와 next를 이용하여 불러줍니다.


 만약 iterable하더라도 iterator가 아닙니다.

x = [1,2,3,4,5]
next(x)
Traceback (most recent call last):
	File .....
	
iterx = iter(x)
next(iterx)
#1


그리고 이제 list를 통해 예시를 설명하도록 하겠습니다.

아래의 소스를 보면 type이 어떻게 정의도는 지를 알 수 있습니다.

x = [1,2,3,4,5]
type(x)

#<class 'list'>

iterx = iter(x)
type(iterx)

#<class 'list_iterator'>


그리고 이제 python 3.x, python 2.7.x의 기준으로 next()를 사용하는 법을 보도록 하겠습니다.

x = [1,2]
type(x)

#<class 'list'>

iterx = iter(x)
type(iterx)

#<class 'list_iterator'>

#=-=-=-=-=-=-=-=-=-=-=- python 2 version
iterx.next()
#1
iterx.next()
#2
iterx.next()
#Traceback (most recent ...
#StopIteration
#=-=-=-=-=-=-=-=-=-=-=- python 3 version
next(iterx)
#1
next(iterx)
#2
#Traceback (most recent ...
#StopIteration


list는 1, 2로 이루어져 있으니, 3번째를 가져올 때 StopIteration을 하도록 되어 있습니다.

[그림 01]을 보면 더 확실하게 알 수 있겠습니다. ㅎㅅㅎ

[그림 01] python 2.7.x 와 python 3.x의 next()함수 사용법 비교


자, 그렇다면 next()함수를 통해 다음을 가져온다는 것은 알았는데, 살짝 더 들여다봅시다.

다음 소스는 4개의 list를 iterable한 list(list_iterator)를 구현하고 출력하는 프로그램입니다.

#iterator Test
#python 3 version source

List_first		= [1,2,3,4,5]
List_second 	= ['a','b','c','d']
List_third		= ["Third String", "Is", "List_Third~~"]
List_forth		= [10]

List_sum 		= []

List_sum.append(List_first)
List_sum.append(List_second)
List_sum.append(List_third)
List_sum.append(List_forth)


print("print List_sum : ", List_sum)

iterList = iter(List_sum)

print("\nmake it iterable!!!=-=-=-=-=-=-=-=-\n")

for i in range(4):
	print("print List_next()", next(iterList))



위의 소스를 실행시키면 [그림 02]와 같은 결과를 볼 수 있습니다.


[그림 02] list내의 list를 하나의 iterator로 생성


단순히 list를 가져온다는 것이 아니라 하나의 요소를 가져오는 것으로 이해하시면 더 좋을 것 같아 예시를 넣었습니다.

이러한 iteration은 [그림 03]과 같은 구조로 표현할 수 있습니다.


[그림 03] Iteration 구조



참고 URL : bluese05 님의 글

참고 URL : friberry 님의 글



+ Recent posts