1. 이터러블(iterable), 이터레이터(iterator)

1
2
for string in ['hello', 'thank you', 'and you']:
    print(string)
1
2
3
'hello'
'thank you'
'and you'

위 코드는 파이썬을 이용한 간단한 반복문 입니다. 파이썬에서 반복문은 for in문과 while문이 있습니다. 그 중 for in문은 뒤에 list, range, tuple, set, dictionary 등이 옵니다. 그리고 반복문 안에서 해당 객체 안에 든 값을 차례대로 받습니다. 위 코드에서는 “hello”, “thank you”, “and you” 값을 차례대로 받았습니다. 이렇게 객체 안의 값을 차례대로 받는 것을 객체를 순환한다고(iterate) 합니다. 그리고 이렇게 받은 값을 순환값, 순환 가능한 객체를 이터러블(iterable) 이라고 합니다.

이터러블을 언어적 수준에서 지원해 주지 않는 다른 언어를 먼저 접하신 분들이라면 이터러블을 처음 접했을 시 정말 놀랐을 겁니다(저는 그랬습니다.). 이터러블은 코드를 정말 직관적이고 짧게 만들어 줍니다. 그리고 이터러블을 지원하는 다른 언어들이 그렇듯이 파이썬 또한 이터러블 객체를 직접 만들 수 있습니다. 그렇기 위해서 우선 for in문에서 객체를 어떻게 순환하는지 알아야 합니다.

우선 for in문은 순환할 객체의 __iter__ 메소드를 호출합니다. __iter__메소드는 __next__ 메소드를 가지고 있는 객체를 반환합니다. 이렇게 반환된 객체는 반복문이 반복될때마다 __next__ 메소드를 호출합니다. 그리고 __next__ 메소드의 반환값이 순환값으로 사용됩니다. 이터레이터는 더이상 순환할 값이 없을 경우 StopIteration 에러를 일으킵니다. for in 문은 이를 감지하고 반복문을 마칩니다.

즉, 파이썬에서 이터러블 객체는 __iter__ 이 정의된 객체를 말합니다. 그리고 __next__ 이 정의된 객체를 이터레이터라고 합니다. 이는 상속구조로 정의되는 것이 아니라 메소드의 유무로 결정되는 덕타이핑(duck-typing) 입니다. 이 과정에 맞추어 흔히들 쓰는 ragne 클래스를 구현하면 다음과 같습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyRange:
	def __init__(self, max):
		self.max

	def __iter__(self):
		return MyRangeIterator(self.max)

class MyRangeIterator:
	def __init__(self, num):
		self.max = max
		self.current = -1
	
	def __next__(self):
		self.current += 1
		if self.current >= num:
			raise StopIteration
		return self.current

	def __iter__(self):
		return self

for i in MyRange(5):
	print(i)
1
2
3
4
5
0
1
2
3
4

코드를 보시면 아시겠지만 __next__메소드가 StopIteration을 일으키면 이터레이터 객체는 더 이상 값을 순환하지 않습니다. 즉 사용하는 의미가 없어집니다. 재사용이 불가능합니다. 한번더 값을 순환하고 싶으면 이터러블로 부터 새로운 이터레이터를 얻어야 합니다. 그렇기 때문에 순환하는 특징이 필요한 클래스는 이터레이터가 아닌 이터러블을 전달값을 받고 받은 이터러블에서 이터레이터를 얻습니다.

또 다른 특징으로는 이터레이터 또한 __iter__ 메소드가 정의되어있고 스스로를 반환합니다. 이는 위에서 말한 순환하는 특징이 필요한 클래스에 이터레이터 객체도 사용할 수 있게 해줍니다. 또한 이러한 특징을 필요로 하는 클래스는 대부분 이터레이터 입니다. 이는 다음과 같은 사용을 가능하게 해줍니다.

1
2
for i in filter(lambda i, x: i % 2 == 0, enumerate(range(6))):
    print(i)
1
2
3
0
2
4