본문 바로가기
python

파이썬 기초 - Iterable, Iterator, Generator

by kyeongseo.oh 2025. 3. 18.

Python의 Iterable, Iterator, Generator

Iteration

Iteration은 여러 항목을 하나씩 차례대로 처리하는 것을 말한다.

fruits = ["사과", "바나나", "딸기"]
for fruit in fruits:
    print(fruit)

위 예제에서 "사과", "바나나", "딸기"를 하나씩 출력한다. 이것이 가능한 이유는 리스트가 iterable이기 때문이다.

 

Iterable

Iterable은 쉽게 말해 "반복할 수 있는 것"으로, __iter__() 메서드를 가진 모든 객체가 여기에 해당한다.

대표적인 Iterable 종류

  • 리스트(list): [1, 2, 3]
  • 튜플(tuple): (1, 2, 3)
  • 문자열(string): "hello"
  • 딕셔너리(dict): {"a": 1, "b": 2}
  • 세트(set): {1, 2, 3}
  • 파일 객체(file object)

동작 방식

Iterable은 __iter__() 메서드를 가지고 있어, 이 메서드를 호출하면 Iterator를 반환한다.

사용하는 경우

  • 여러 항목을 담고 있는 컬렉션을 다룰 때 - 리스트, 튜플, 세트, 딕셔너리와 같이 여러 데이터를 하나의 변수에 모아서 관리하고 싶을 때 사용한다.
  • for 루프로 항목을 하나씩 처리하고 싶을 때 - 데이터 집합의 각 요소를 반복적으로 처리해야 할 경우 사용한다.
  • 데이터를 순서대로 접근하고 싶을 때 - 인덱스 기반 접근이 필요한 경우 (리스트, 튜플) 사용한다.

예제

from typing import Iterable

my_list = [1, 2, 3]
my_tuple = (1, 2, 3)
my_str = "hello"
my_dict = {"a": 1, "b": 2}
my_set = {1, 2, 3}

# Iterable은 for 루프로 순회할 수 있다.
for item in my_list:
    print(item)

# Iterable인지 확인
print(isinstance(my_list, Iterable))
print(isinstance(my_tuple, Iterable))
print(isinstance(my_str, Iterable))
print(isinstance(my_dict, Iterable))
print(isinstance(my_set, Iterable))

 

 

Iterator

Iterator는 "다음 항목을 반환하는 역할"을 하는 객체다. Iterable에서 항목을 하나씩 가져오는 작업을 담당한다.

특징

  • 현재 위치를 기억한다.
  • __next__() 메서드로 다음 항목을 가져온다.
  • 더 이상 항목이 없으면 StopIteration 예외를 발생시킨다.
  • 한번 끝까지 순회하면 재사용할 수 없다.

동작 방식

  1. iter() 함수로 iterable에서 iterator를 얻는다.
  2. next() 함수로 다음 항목을 가져온다.
  3. 항목이 없으면 StopIteration 예외가 발생한다.
my_list = [1, 2, 3]
my_iterator = iter(my_list)  # 또는 my_list.__iter__() iterator 생성

print(next(my_iterator))  # 1
print(next(my_iterator))  # 2
print(next(my_iterator))  # 3
print(next(my_iterator))  # StopIteration 예외 발생

# 이미 사용해서 아무것도 출력되지 않음
for i in my_iterator:
    print(i)

사용하는 경우

  • 데이터를 한 번에 한 항목씩 처리하고 싶을 때
  • 메모리를 효율적으로 사용하고 싶을 때 (전체 데이터를 메모리에 로드하지 않음)
  • 현재 위치를 관리해야 할 때

예제: CountUp Iterator 만들기

class CountUp:
    def __init__(self, max_num):
        self.max_num = max_num
        self.current = 0
    
    def __iter__(self):
        # 자기 자신을 iterator로 반환
        return self
    
    def __next__(self):
        if self.current < self.max_num:
            self.current += 1
            return self.current
        else:
            # 끝에 도달하면 StopIteration 발생
            raise StopIteration

counter = CountUp(3)
for num in counter:
    print(num)

예제: CountDown Iterator 만들기

class CountDown:
    def __init__(self, max_num):
        self.current = max_num + 1
    
    def __iter__(self):
        # 자기 자신을 iterator로 반환
        return self
    
    def __next__(self):
        if self.current > 1:
            self.current -= 1
            return self.current
        else:
            # 끝에 도달하면 StopIteration 발생
            raise StopIteration

counter = CountDown(3)
for num in counter:
    print(num)

 

Generator

Generator는 "iterator를 쉽게 만들 수 있는 함수"로, yield 키워드를 사용해 값을 하나씩 반환한다.

특징

  • 함수처럼 정의하지만 return 대신 yield를 사용한다.
  • 호출하면 generator 객체(iterator의 일종)를 반환한다.
  • 현재 상태를 자동으로 기억한다.
  • 필요할 때만 값을 계산한다. (지연 평가, lazy evaluation)

동작 방식

  1. Generator 함수를 호출하면 generator 객체가 반환된다.
  2. next() 함수가 호출되면 다음 yield 문까지 실행된다.
  3. yield에서 값을 반환하고 함수 실행을 일시 중지한다.
  4. 다시 next()가 호출되면 중지된 지점부터 실행을 재개한다.

사용하는 경우

  • Iterator를 간단하게 만들고 싶을 때
  • 메모리 효율성이 중요할 때 (큰 데이터셋 처리)
  • 무한 시퀀스를 다룰 때
  • 복잡한 상태 관리를 단순화하고 싶을 때

예제: Generator 함수

def count_up(max_num):
    current = 0
    while current < max_num:
        current += 1
        yield current

# 사용 예
for num in count_up(3):
    print(num)  # 1, 2, 3 출력

예제: Generator 표현식

Generator 표현식은 리스트 컴프리헨션과 비슷하지만 괄호 () 를 사용한다.

import sys

# 리스트 컴프리헨션 (모든 값을 메모리에 저장)
squares_list = [x*x for x in range(1000000)]  # 메모리 많이 사용

# Generator 표현식 (필요할 때만 계산)
squares_gen = (x*x for x in range(1000000))   # 메모리 적게 사용

# 앞의 5개 항목만 출력
for i, square in enumerate(squares_gen):
    if i >= 5:
        break
    print(square)  # 0, 1, 4, 9, 16 출력
    
print("List Comprehension Size:", sys.getsizeof(squares_list), "bytes")
# List Comprehension Size: 8448728 bytes

print("Generator Expression Size:", sys.getsizeof(squares_gen), "bytes")
# Generator Expression Size: 112 bytes

예제: 대용량 파일 처리

메모리가 충분하지 않아 전체 파일을 한 번에 로드할 수 없을 때 사용할 수 있다.

def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:  # 파일 객체는 iterable
            yield line.strip()

# 예시: 대용량 로그 파일에서 에러만 필터링
for line in read_large_file('huge_log.txt'):
    if 'ERROR' in line:
        print(f"오류 발견: {line}")

예제: 데이터 처리 파이프라인

데이터 처리 단계를 연결할 때 사용한다.

def get_numbers():
    """숫자 생성"""
    for i in range(1, 11):
        yield i

def filter_even(numbers):
    """짝수만 필터링"""
    for num in numbers:
        if num % 2 == 0:
            yield num

def multiply_by_ten(numbers):
    """10을 곱함"""
    for num in numbers:
        yield num * 10

# 파이프라인 구축
numbers = get_numbers()
even_numbers = filter_even(numbers)
result = multiply_by_ten(even_numbers)

# 결과 출력
for num in result:
    print(num)  # 20, 40, 60, 80, 100 출력

예제: 무한 시퀀스 처리

종료 조건 없이 계속 값을 생성할 때 사용한다.

def fibonacci():
    """무한 피보나치 수열 생성기"""
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 처음 10개 피보나치 수만 출력
fib_gen = fibonacci()
for _ in range(10):
    print(next(fib_gen))  # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

 

비교: Iterable vs Iterator vs Generator

종류 Iterable  Iterator  Generator
정의 순회 가능한 객체 순회 상태를 관리하는 객체 Iterator를 생성하는 함수
예시 리스트, 튜플, 문자열 iter() 함수로 반환되는 객체 yield를 사용하는 함수
메서드 __iter__() __iter__(), __next__() 자동으로 구현됨
재사용 가능 (여러 번 순회 가능) 불가능 (한 번만 사용) 불가능 (한 번만 사용)
값의 계산 미리 계산됨 요청 시 계산됨 요청 시 계산됨
메모리 모든 항목 저장 현재 상태만 저장 현재 상태만 저장

 

댓글