Python - Iterators

데이터 컬렉션의 요소들을 순차적으로 접근할 수 있게 해주는 객체

이터레이터의 기능과 역할

  1. 데이터 스트림에서 한 번에 하나의 항목을 반환
  2. 현재 항목과 방문한 항목을 추적
  3. 컨테이너의 요소에 순차적으로 접근
  4. 메모리를 효율적으로 사용

이터레이터의 특징

  1. __iter__()__next__() 메서드를 구현하여 이터레이터 프로토콜을 따름
  2. next() 함수를 사용하여 다음 요소에 접근
  3. 모든 요소를 순회한 후 StopIteration 예외 발생
  4. 지연 평가(lazy evaluation)를 지원하여 필요한 요소만 생성

활용

  • 대용량 파일 처리
  • 데이터베이스 쿼리 결과 처리
  • 스트리밍 데이터 처리
  • 메모리 효율적인 데이터 처리
  • 실시간 데이터 생성

이터레이터의 종류

  1. 기본 이터레이터
    가장 일반적인 형태의 이터레이터.
    __iter____next__ 메서드를 구현하여 만든다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    class NumberIterator:
        def __init__(self, start, end):
            self.current = start
            self.end = end
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.current > self.end:
                raise StopIteration
            else:
                self.current += 1
                return self.current - 1
    
    # 사용 예
    numbers = NumberIterator(1, 5)
    for num in numbers:
        print(num)
    

    이 예제는 1부터 5까지의 숫자를 생성하는 기본 이터레이터를 보여준다.

  2. 제너레이터 이터레이터
    제너레이터 함수를 사용하여 간단하게 이터레이터를 만들 수 있다.
    yield 키워드를 사용하여 값을 하나씩 반환

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    # 이 예제는 1부터 5까지의 숫자의 제곱을 생성하는 제너레이터 이터레이터를 보여준다.
    def square_numbers(max):
        for i in range(1, max + 1):
            yield i * i
    
    # 사용 예
    squares = square_numbers(5)
    for square in squares:
        print(square)
    
    # 피보나치 수열
    def fibonacci(limit):
        a, b = 0, 1
        while a < limit:
            yield a
            a, b = b, a + b
    
    for num in fibonacci(10):
        print(num)  # 0, 1, 1, 2, 3, 5, 8이 출력됩니다
    
  3. 역방향 이터레이터
    컬렉션을 역순으로 순회하는 이터레이터.

    1
    2
    3
    4
    
    names = ['ram', 'shyam', 'ajay', 'bipin', 'manoj', 'alex']
    reverse_iter = reversed(names)
    for name in reverse_iter:
        print(name)
    

    이 예제는 리스트의 요소를 역순으로 출력.

  4. 인덱스 기반 이터레이터
    range() 함수를 사용하여 인덱스를 기반으로 순회하는 이터레이터.

    1
    2
    3
    4
    
    names = ['ram', 'shyam', 'ajay', 'bipin', 'manoj', 'alex']
    for i in range(len(names)):
        names[i] = names[i].upper()
        print(names[i])
    

    이 예제는 리스트의 각 요소를 대문자로 변환하면서 출력.

  5. 열거 이터레이터
    enumerate() 함수를 사용하여 인덱스와 값을 함께 순회하는 이터레이터.

    1
    2
    3
    
    names = ['ram', 'shyam', 'ajay', 'bipin', 'manoj', 'alex']
    for index, value in enumerate(names):
        print(f"Index: {index}, Value: {value}")
    

    이 예제는 리스트의 각 요소의 인덱스와 값을 함께 출력.

  6. 무한 이터레이터
    끝나지 않는 시퀀스를 생성하는 이터레이터.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    class InfiniteNumbers:
        def __iter__(self):
            self.num = 0
            return self
    
        def __next__(self):
            self.num += 1
            return self.num
    
    # 사용 예
    infinite = InfiniteNumbers()
    iterator = iter(infinite)
    for _ in range(5):  # 처음 5개의 숫자만 출력
        print(next(iterator))
    
  7. 커스텀 이터레이터
    특정 용도에 맞는 커스텀 이터레이터를 만들 수 있다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    from datetime import datetime, timedelta
    
    class DateRange:
        def __init__(self, start_date, end_date):
            self.start_date = start_date
            self.end_date = end_date
            self.current_date = start_date
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.current_date > self.end_date:
                raise StopIteration
    
            current = self.current_date
            self.current_date += timedelta(days=1)
            return current.strftime('%Y-%m-%d')
    
    start = datetime(2024, 1, 1)
    end = datetime(2024, 1, 5)
    date_range = DateRange(start, end)
    for date in date_range:
        print(date)  # 2024-01-01부터 2024-01-05까지 출력
    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

### 이터레이터 도구

```python
from itertools import islice, takewhile, dropwhile, chain, count, cycle, repeat

# 무한 카운터
counter = count(1)  # 1부터 시작하는 무한 수열
# 순환 이터레이터
seasons = cycle(['봄', '여름', '가을', '겨울'])
# 반복 이터레이터
constant = repeat(42, times=3)  # 42를 3번 반복

# 이터레이터 체이닝
# 여러 이터레이터를 하나로 연결
numbers = range(1, 4)  # 1, 2, 3
letters = 'ABC'
combined = chain(numbers, letters)  # 1, 2, 3, 'A', 'B', 'C'

# 데이터 필터링과 슬라이싱을 위한 이터레이터
numbers = range(10)  # 0부터 9까지
first_five = islice(numbers, 5)  # 처음 5개 요소
less_than_five = takewhile(lambda x: x < 5, numbers)  # 5보다 작은 요소들
drop_less_than_five = dropwhile(lambda x: x < 5, numbers)  # 5 이상인 요소들부터

# islice로 처음 5개 요소 가져오기
print(list(first_five))  # [0, 1, 2, 3, 4]

# takewhile로 조건에 맞는 요소들 가져오기
print(list(less_than_five))  # [0, 1, 2, 3, 4]

# dropwhile로 조건을 만족하지 않는 첫 요소부터 가져오기
print(list(drop_less_than_five))  # [5, 6, 7, 8, 9]

참고 및 출처