이터레이터(Iterator) 와 제너레이터(Generator) 는 파이썬에서 순회 가능한(iterable) 객체를 다룰 때 중요한 개념입니다.
정의
__iter__() 메서드와 __next__() 메서드를 구현한 객체.__iter__() 는 자신을 반환하고, __next__() 는 다음 값을 반환하다가 더 이상 값이 없으면 StopIteration 예외를 발생시킵니다.사용 예시
class Counter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self):
if self.current > self.high:
raise StopIteration
val = self.current
self.current += 1
return val
for num in Counter(1, 3):
print(num)
# 출력:
# 1
# 2
# 3
특징
정의
yield 키워드를 사용하여 제너레이터 객체를 반환하는 특별한 형태의 이터러블yield 지점까지 실행된 뒤 값을 하나씩 반환하며 상태를 유지사용 예시
def count_up_to(n):
current = 1
while current <= n:
yield current
current += 1
for num in count_up_to(3):
print(num)
# 출력:
# 1
# 2
# 3
장점
return 대신 StopIteration이 자동 발생| 구분 | Iterator 클래스 | Generator 함수 |
|---|---|---|
| 정의 방식 | class로 __iter__, __next__ 구현 | def 함수에 yield 사용 |
| 코드 복잡도 | 메서드 여러 개 작성 필요 | 한 줄 yield로 간편 |
| 상태 유지 | 직접 변수로 관리 | 파이썬이 자동으로 상태 보존 |
| 예외 처리 | StopIteration 직접 발생 | return 또는 함수 종료 시 자동 발생 |
reset() 등)를 함께 제공하고 싶을 때__iter__, __next__)을 구현한 객체yield를 이용해 간단히 이터레이터를 생성하는 함수map() & filter()의 활용파이썬의 map()과 filter() 함수는 둘 다 지연 평가(lazy evaluation) 방식으로 동작하는 이터러블(순회 가능한) 객체를 반환하기 때문에, 이터레이터나 제너레이터와 함께 쓰면 메모리를 절약하면서도 필요한 시점에만 계산을 수행할 수 있습니다.
map()map 객체 (이터레이터)# 예시 1: 리스트에 적용
nums = [1, 2, 3, 4]
doubled = map(lambda x: x * 2, nums)
print(type(doubled)) # <class 'map'>
print(list(doubled)) # [2, 4, 6, 8]
# 예시 2: 제너레이터와 함께 사용
def count_up_to(n):
for i in range(1, n+1):
yield i
gen = count_up_to(5) # 1,2,3,4,5 생성
mapped = map(lambda x: x**2, gen) # 제곱 값만 차례대로
for val in mapped:
print(val)
# 출력: 1 4 9 16 25
gen 자체도 이터레이터이기 때문에, map()이 내부에서 __iter__()→__next__() 를 호출하며 한 번에 한 요소씩 처리합니다.filter()filter 객체 (이터레이터)# 예시 1: 리스트에서 짝수만 걸러내기
nums = [1, 2, 3, 4, 5, 6]
evens = filter(lambda x: x % 2 == 0, nums)
print(list(evens)) # [2, 4, 6]
# 예시 2: 제너레이터와 함께 사용
def infinite_numbers():
i = 1
while True:
yield i
i += 1
gen_inf = infinite_numbers()
filtered = filter(lambda x: x % 3 == 0, gen_inf) # 여기서 filtered는 이터레이터 객체. list가 아님.
# 무한 제너레이터라도 filter 로직으로 조건 만족 시점만 소비
import itertools
for val in itertools.islice(filtered, 5):
print(val)
# 출력: 3 6 9 12 15
filter()로 원하는 개수만큼만 꺼내 쓰면 무한 루프에 빠지지 않고 필요한 만큼만 처리할 수 있습니다.map()·filter() 연쇄 사용def ints():
yield from range(1, 11)
# 1) 1~10 → 짝수만 걸러 → 제곱 → 출력
# pipeline = (x**2 for x in ints() if x % 2 == 0)
pipeline = map(lambda x: x**2,
filter(lambda x: x % 2 == 0,
ints()))
print(list(pipeline)) # print pipeline(iterator object) as a form of list
# [4, 16, 36, 64, 100]
내부적으로
ints() 제너레이터가 값을 하나씩 꺼내고filter()가 짝수만 통과시키며map()이 제곱을 수행모두 지연 평가 되므로, 중간 결과가 메모리에 쌓이지 않습니다.
재사용 불가
map/filter 객체는 소진되어 버리므로, 재사용하려면 다시 생성해야 합니다.가독성
# 같은 동작을 제너레이터 표현식으로
pipeline = (x**2 for x in ints() if x % 2 == 0)
map()과 filter()는 이터레이터/제너레이터와 결합해 메모리 효율과 지연 평가 이점을 제공