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 예외를 발생시킨다.
- 한번 끝까지 순회하면 재사용할 수 없다.
동작 방식
- iter() 함수로 iterable에서 iterator를 얻는다.
- next() 함수로 다음 항목을 가져온다.
- 항목이 없으면 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)
동작 방식
- Generator 함수를 호출하면 generator 객체가 반환된다.
- next() 함수가 호출되면 다음 yield 문까지 실행된다.
- yield에서 값을 반환하고 함수 실행을 일시 중지한다.
- 다시 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__() | 자동으로 구현됨 |
재사용 | 가능 (여러 번 순회 가능) | 불가능 (한 번만 사용) | 불가능 (한 번만 사용) |
값의 계산 | 미리 계산됨 | 요청 시 계산됨 | 요청 시 계산됨 |
메모리 | 모든 항목 저장 | 현재 상태만 저장 | 현재 상태만 저장 |
'python' 카테고리의 다른 글
파이썬 기초 - Context Manager (0) | 2025.03.19 |
---|---|
파이썬 기초 - Mutable과 Immutable 객체 (0) | 2025.03.19 |
파이썬 기초 - 클래스 기초 (0) | 2025.03.16 |
파이썬 기초 - 함수 기초 (0) | 2025.03.16 |
파이썬 기초 - 조건문과 반복문 (1) | 2025.03.16 |
댓글