Iterable, Iterator 모두 내장 모듈 collections.abc에 정의되어 있는 추상 클래스(ABC)이다. 따라서 하위 클래스에서 구현해야할 메서드를 __abstractmethods__에 가지고 있다. 또한 Iterator는 Iterable을 상속받는 하위 클래스로 Iterator는 Iterable의 특징을 모두 가진다.
# Iterable의 하위 클래스는 __iter__ 메서드를 구현해야함
print(Iterable.__abstractmethods__)
>>> frozenset({'__iter__'})
# Iterator의 하위 클래스는 __next__ 메서드를 구현해야함
print(Iterator.__abstractmethods__)
>>> frozenset({'__next__'})
# Iterator ⊂ Iterable
print(issubclass(Iterator, Iterable))
>>> True
for문의 in 다음에 올 수 있는 모든 객체 (str, list, dict ... )__iter__ 추상 메서드가 있어야한다__iter__ 메서드가 클래스에 있으면 해당 객체에 iter() 내장함수를 적용하여 iterator를 생성할 수 있다iter() 내장함수는 Iterable을 인자로 실행될 때마다 새로운 Iterator 인스턴스를 반환한다li = [1, 2, 3, 4]
di = {'a': 1, 'b': 2}
li_iter_1 = iter(li)
li_iter_2 = iter(li)
di_iter = iter(di)
rg_iter = iter(range(10))
print(li_iter_1)
print(li_iter_2)
print(di_iter)
print(rg_iter)
>>> <list_iterator object at 0x000002297E3DBB20>
>>> <list_iterator object at 0x000002297E3DBB50> # 위와 다른 Iterator 인스턴스 반환
>>> <dict_keyiterator object at 0x000002297E702430>
>>> <range_iterator object at 0x000001EB992EBD70> # range()의 결과도 Iterable
__iter__ 추상 메서드가 있어야한다iter() 내장함수는 Iterator가 인자인 경우 자기 자신 반환__next__ 추상 메서드가 있어야한다next() 내장함수의 인자로 줬을 때 다음에 반환할 값을 정의해야 한다next() 내장함수 : Iterator의 다음 인자를 반환하고 상태(위치)를 다음으로 옮기는 기능itr = iter([1, 2, 3, 4])
print(next(itr))
>>> 1 # 다음 반환 인자 = 2
for i in itr:
print(i)
>>> 2
>>> 3
>>> 4
for i in itr:
print(i)
>>> # exception 발생은 안하지만 아무것도 나오지 않음
print(next(itr))
>>> StopIteration # 더 반환할 값이 없음. 재활용 불가
class MyIterable:
def __iter__(self):
pass
class MyIterator:
def __iter__(self):
pass
def __next__(self):
pass
print(issubclass(MyIterable, Iterable))
>>> True
print(issubclass(MyIterator, Iterator))
>>> True
일단 위처럼 __iter__, __next__ 메서드를 정의해놓기만 하면 파이썬에서 Iterable, Iterator로 인식하기는 한다. 하지만 의미상으로도 맞지 않고, 쓸모도 없으므로 개념적 정의에 맞게 수정해보자.
class MyIterable:
def __init__(self, n):
self.n = n
def __iter__(self):
return MyIterator(self.n)
class MyIterator:
def __init__(self, n):
self.n = n
self.cur = 0
def __iter__(self):
return self
def __next__(self):
if self.cur >= self.n:
raise StopIteration
self.cur += 1
return self.cur
my_iterable = MyIterable(3)
my_iterator = iter(my_iterable)
위의 생성규칙을 토대로 직접 Iterable, Iterator를 만들어 사용할 수 있다.
for 문의 동작for 문의 동작 방식은 Iterable, Iterator와 깊은 관계가 있다. for 문의 아래와 같은 과정을 거쳐 순회를 수행한다.
in 뒤에 오는 Iterable에 iter() 내장함수를 써서 Iterator를 얻는다iter(Iterable) -> 새로운 Iterator, iter(Iterator) -> 자기 자신next() 내장함수를 통해 반환값 하나씩 얻기next()의 결과로 StopIteration 예외가 발생하면 for 문 종료