이터레이터(Iterator)

16143 단어 iteratorpythoniterator

iterate: 반복하다

이터레이터

대부분의 프로그래밍 언어에는 for 루프처럼 반복문이 존재하는데, 일반적으로 배열을 대상으로 반복문을 사용합니다.

파이썬에서는 list, tuple, str, set 등 다양한 컨테이너 객체에 대해 반복문을 사용할 수 있습니다.
이들은 공통점이 있는데, 바로 내부적으로 __iter__ 메서드를 가지고 있다는 것입니다.

파이썬에서는 모든 것이 객체입니다.
반복문을 사용할 때 아마 가장 많이 사용할 list, tuple 형태의 데이터도 결국엔 객체이며, 이 안에 __iter__ 메서드가 내장되어 있는 것을 직접 확인할 수 있습니다.

>>> dir([1, 2, 3])
['__add__', '__class__', '__class_getitem__', '__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']

따라서 이 메서드의 존재는 해당 객체가 반복 가능하다는 (반복문의 대상이 될 수 있는) 성질을 갖고 있다고 알려주는 셈입니다.

__iter__ 메서드에는 해당 객체를 대상으로 반복하는 방법을 정의해야 합니다.
그렇다면 이 메서드를 어떤 식으로 구현해야 제대로 동작할까요?

__iter__ 메서드는 __next__ 메서드를 가지고 있는 객체를 반환해야 합니다.
조금 복잡하게 들릴 수 있지만, __next__ 메서드는 다음 순서의 값을 반환하는 메서드일 뿐입니다.

간단한 예시가 아래에 있습니다.

class Counter:
    """__next__ 메서드를 구현함"""

    def __init__(self):
        self.count = 0
    
    def __next__(self):
        """다음 순서의 값을 반환함"""
        self.count += 1
        if self.count > 10:
            raise StopIteration
        return self.count


class Iterable:
    """__iter__ 메서드를 가지므로 반복문의 대상이 될 수 있음"""

    def __iter__(self):
        """__next__ 메서드를 가지는 객체를 반환함"""
        return Counter()


if __name__ == "__main__":
    iterable_obj = Iterable()
    for x in iterable_obj:
        print(x)

Counter 클래스의 __next__ 메서드는 앞서 언급한 대로 다음 순서의 값을 반환하는 역할을 수행합니다. 만약 더 이상 반환할 값이 없다면 StopIteration 예외를 일으켜 반복의 끝을 알려야 합니다.

이보다 더 간단한 예시로 아래처럼 __iter__ 메서드와 __next__ 메서드를 한 클래스에서 구현해도 되지만 멀티 프로세싱과 같은 동시성 기법을 사용할 때 문제가 될 수 있다고 합니다.

class Iterable:
    def __init__(self):
        self.count = 0
    
    def __next__(self):
        self.count += 1
        if self.count > 10:
            raise StopIteration
        return self.count

    def __iter__(self):
        return self


if __name__ == "__main__":
    iterable_obj = Iterable()
    for x in iterable_obj:
        print(x)

빌트인(built-in) 함수

__next__ 메서드와 __iter__ 메서드는 여타 다른 매직 메서드 (또는 던더 메서드)와 마찬가지로 빌트인 함수를 제공합니다.

iter() 함수는 주어진 대상(컬렉션)에 요소 하나씩 접근하는 이터레이터를 생성하며, 안에는 __next__() 메서드가 구현됩니다. 즉, __iter__ 메서드와 __next__ 메서드 모두 하나의 객체에서 자동으로 구현되어 생성됩니다.

>>> s = 'abc'
>>> it = iter(s)
>>> it
<str_iterator object at 0x1102df370>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> dir(it)
['__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__']

정리

  • __iter__ 메서드를 가지는 않는 객체라면 반복 가능한 객체라고 부를 수 없습니다.
  • 실제로 반복이 가능한 객체가 되려면 __next__ 메서드를 제공해야 합니다.
  • iter() 함수와 next() 함수 또한 빌트인 함수로서 제공됩니다.

출처

좋은 웹페이지 즐겨찾기