[Python] Iterable과 Iterator
Iterable과 Iterator에 대한 정리
환경
- Python
Iterable과 Iterator의 정의
Iterable
- 멤버들을 한번에 하나씩 돌려줄 수 있는 객체
- 문서에는
An object capable of returning its members one at a time.
으로 나와있다. - “이터러블의 예로는 모든 (list, str, tuple 같은) 시퀀스 형들, dict 같은 몇몇 비 시퀀스 형들, 파일 객체들,
__iter__()
나 시퀀스 개념을 구현하는__getitem__()
메서드를 써서 정의한 모든 클래스의 객체들이 있습니다.” - 이터러블 객체가 내장 함수
iter()
에 인자로 전달되면, 그 객체의 이터레이터를 돌려줍니다.
>>> a = [1,2,3]
>>> type(a)
<class 'list'>
>>> a_iterator = iter(a)
>>> type(a_iterator)
<class 'list_iterator'>
Iterator
- 데이터의 스트림을 표현하는 객체
- 문서에는
An object representing a stream of data.
라고 나와있다. - 이터레이터의
__next__()
메서드를 통해 반복적으로 호출하면 스트림의 데이터를 차례대로 돌려준다. 더 이상의 데이터가 없으면StopIteration
예외를 발생시킨다. - 이터레이터는 이터레이터 객체 자신을 돌려주는
__iter__()
메서드를 가지기 때문에, 이터레이터는 이터러블이기도 하다.
>>> a = [1,2,3]
>>> type(a)
<class 'list'>
>>> a_iterator = iter(a)
>>> type(a_iterator)
<class 'list_iterator'>
>>> dir(a_iterator)
['__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__']
이터레이터 형(Iterator Types)
이터레이터 프로토콜
- 파이썬은 이터레이션 개념을 지원한다. 이것은 아래 두 개의 메서드(
iterator.__iter__()
와iterator.__next__()
)를 사용해서 구현되며 이것들은 사용자 정의 클래스가 이터레이션을 지원할 수 있도록 하는데 사용된다. - 문서에서는 컨테이너에 대한 예제만 나와있는데 글을 읽어보면 임의의 클래스나 객체에 대해서도 구현이 가능해보인다.
- 이터레이터 객체 자체는 다음과 같은 두 가지 메서드를 지원해야 하는데, 둘이 함께 이터레이터 프로토콜 (iterator protocol) 를 이룬다.
iterator.__iter__()
- 이터레이터 객체 자신을 돌려준다.
iterator.__next__()
- 객체의 다음 항목을 돌려준다. 더 항목이 없으면 StopIteration 예외를 일으킨다.
컨테이너에서의 이터레이션
- 컨테이너 객체가 이터레이션 지원을 제공하려면 아래에 있는 한 가지 메서드를 정의해야한다.
- 위에 이터레이터 프로토콜에 따라서 바로 구현해도 되는거 같다. (글쓴이의 추측)
container.__iter__()
- 이터레이터 객체를 돌려준다. 이 객체는 위에서 설명하는 이터레이터 프로토콜을 지원해야 한다.
추가 궁금한 사항
Iterable과 Iterator의 관계
- 상단에 정리를 해두었지만 반복을 통해 멤버들을 한번에 하나씩 돌려줄 수 있는 객체들을 모두 통틀어서 Iterable이라고 하고 그
Iterable
를 구현하는 방법중 하나가Iterator
를 사용한거다. Iterable
로 만들고자하는 클래스에__iter()__
메서드를 구현해서Iterator
를 반환하도록 만들고 그Iterator
는 상단 이터레이터 형(Iterator Types)에 나온 이터레이터 프로토콜에 따라 자신을 반환하는__iter()__
와 다음 항목을 돌려주는__next()__
를 구현하면 된다.
__iter__()와 __getitem__()의 차이
- https://stackoverflow.com/questions/20551042/whats-the-difference-between-iter-and-getitem에 따르면
__iter__()
는__getitem__()
보다 나중에 나온 메서드다. __getitem__()
의 경우 인덱스를 통한 접근의 개념이지만__iter__()
의 경우 인덱스 없이도 클래스를 이터레이터 프로토콜에 맞게 구현해 이터러블하게 만들 수 있다.
정리 및 마무리
처음에는 Iterable
과 Iterator
가 뭔지가 궁금했는데 알면 알수록 조금 복잡했었다.
정리하면 Iterable
은 멤버들을 한번에 하나씩 돌려줄 수 있는 객체들을 말하며 list, str, dict, 파일 객체 그리고 __iter__()
나 시퀀스 개념을 구현하는 __getitem__()
를 통해 구현한 객체들이 그 예시이다. 반복을 통해 속한 멤버들을 하나씩 반환할 수 있는 특징을 가진 객체들을 통칭한다고 보면 될거 같다.
이제 Iterator
는 데이터의 스트림을 표현하는 객체로 이터레이터 프로토콜에 따라 두 가지 메서드(iterator.__iter__()
와 iterator.__next__()
)를 구현한 객체이다. Iterator
는 __iter__()
메서드가 구현되어 있기 때문에 Iterable
하기도 하다. 이 Iterator
를 이용해 Iterable
한 객체를 만들 수 있다.
실제 리스트를 보면 __iter__()
메서드를 직접 호출하거나 리스트를 내장 함수 iter()
에 넘겨주면 Iterator
를 반환하며 이 Iterator
내부에는 두 가지 메서드(iterator.__iter__()
와 iterator.__next__()
)가 정의되어 있다.
a = [1, 2, 3]
print(type(a))
print(type(a.__iter__()))
print(type(iter(a)))
print()
print(dir(a))
print(dir(a.__iter__()))
print(dir(iter(a)))
<class 'list'>
<class 'list_iterator'>
<class 'list_iterator'>
['__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']
['__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__']
['__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__']
참고자료
- https://docs.python.org/3/glossary.html
- https://docs.python.org/ko/3/glossary.html
- https://docs.python.org/3/library/stdtypes.html#typeiter
- https://docs.python.org/ko/3/library/stdtypes.html#typeiter
- https://stackoverflow.com/questions/11575925/what-exactly-are-containers-in-python-and-what-are-all-the-python-container
- https://stackoverflow.com/questions/20551042/whats-the-difference-between-iter-and-getitem