이터레이션(Iteration)
어떤 자료 구조(예: 리스트, 튜플 등)를 순차적으로 하나씩 꺼내는 과정
이터러블(Iterable)
내부 요소를 하나씩 꺼낼 수 있는 객체
예: 리스트(list), 튜플(tuple), 문자열(str), 딕셔너리(dict), 셋(set), 그리고 사용자 정의 이터러블 클래스 등
이터러블 객체는 내부적으로 __iter__ 메서드를 구현해야 함
이터레이터(Iterator)
실제로 값을 하나씩 꺼내는 역할을 하는 객체
내부적으로 __next__ 메서드를 가져야 하며, 이 메서드는 순회할 다음 값을 반환
더 이상 꺼낼 값이 없다면 StopIteration 예외를 발생시켜 이터레이션이 종료됨을 알림
# 리스트(이터러블)를 이터레이터로 만드는 과정
numbers = [1, 2, 3]
iterator = iter(numbers) # numbers.__iter__() 호출
print(next(iterator)) # iterator.__next__() 호출 → 1
print(next(iterator)) # 2
print(next(iterator)) # 3
print(next(iterator)) # StopIteration 예외 발생
iter(numbers) → 이터레이터를 반환
next(iterator) → 이터레이터에서 다음 요소를 꺼냄
사용자 정의 이터레이터
직접 클래스를 정의해서 __iter__()와 __next__() 메서드를 구현하면, 원하는 규칙으로 순회를 제어 가능
class Counter:
def __init__(self, start, stop):
self.current = start
self.stop = stop
def __iter__(self):
return self # 이터레이터 자체를 반환
def __next__(self):
if self.current < self.stop:
val = self.current
self.current += 1
return val
else:
raise StopIteration
counter = Counter(1, 5)
for num in counter:
print(num, end=" ")
# 출력: 1 2 3 4
for num in counter: 구문이 내부적으로 __iter__()로 이터레이터를 얻고, 매 반복마다 __next__()를 호출
StopIteration 예외가 발생하면 for 루프가 종료
--🐢
제너레이터(Generator)
이터레이터를 간편하게 만드는 특별한 함수(혹은 표현식)
일반 함수와 달리, return 대신 yield를 사용하여 데이터를 하나씩 반환(생성)
제너레이터 함수는 호출될 때 함수 전체가 한 번에 실행되지 않고, yield 키워드를 만날 때마다 현재 상태를 기억한 채 값을 하나 내보냄
def simple_generator():
print("첫 번째 yield 직전")
yield 1
print("두 번째 yield 직전")
yield 2
print("끝")
gen = simple_generator()
print(next(gen)) # "첫 번째 yield 직전" 출력 후 1 반환
print(next(gen)) # "두 번째 yield 직전" 출력 후 2 반환
print(next(gen)) # "끝" 출력 후 StopIteration 발생
yield를 만나면 값을 반환(중간에 함수가 멈춤)하고, 다음 next()가 호출되면 이전 상태를 이어서 진행
제너레이터 함수는 이터레이터 프로토콜(__iter__, __next__)을 자동으로 구현해 줌
즉, 제너레이터 함수를 호출하면 이터레이터 객체가 반환
제너레이터 표현식
# 제너레이터 표현식
gen_exp = (x*2 for x in range(5))
print(gen_exp) # <generator object <genexpr> at 0x...>
print(next(gen_exp)) # 0
print(next(gen_exp)) # 2
...
제너레이터 표현식은 메모리를 절약하며(필요한 값만 순차적으로 생성), 이터레이터로 활용할 수 있음
제너레이터 장점
# 메모리 효율성
한 번에 모든 데이터를 생성하지 않고, 요청될 때(next()가 불릴 때) 하나씩 생성
큰 데이터셋을 다룰 때 특히 유리
# 코드 간결성
이터레이터를 직접 작성하려면 __iter__, __next__를 구현해야 하지만, 제너레이터는 yield만 쓰면 됨
# 지연 평가(Lazy Evaluation)
즉시 모든 요소를 계산하지 않고, 필요한 시점에 값을 생성해 효율적인 계산이 가능
-🐢-
이터레이터/제너레이터 비교
항목 | 이터레이터(Iterator) | 제너레이터(Generator) |
구현 방식 |
__iter__()와 __next__()를 직접 구현 | yield 키워드를 사용한 함수나 표현식 |
사용 편의 |
상대적으로 복잡 | 간단하고 가독성 좋음 |
메모리 사용 |
로직에 따라 다르지만, 중간 버퍼를 크게 쓸 수 있음 | 기본적으로 한 번에 하나씩 생성(Lazy), 메모리 효율적 |
상태 관리 |
클래스 멤버 변수로 유지 | 함수의 지역 변수 상태를 자동으로 유지(스택 프레임을 보관) |
예외 처리 |
직접 StopIteration를 raise | yield 뒤에 더 이상 코드 없으면 내부적으로 StopIteration 발생 처리 |
예시 | 직접 구현한 순회 구조, 복잡한 상태 머신 | 큰 파일 라인 순회, 무한 시퀀스 등 |
🐢--
'언어 > Python' 카테고리의 다른 글
정규 표현식(Regular Expression) (0) | 2025.01.13 |
---|---|
파이썬 타입 어노테이션 (0) | 2025.01.11 |
클로저/데코레이터 (1) | 2025.01.09 |
유니코드 문자열 (1) | 2025.01.08 |
Threading 모듈 (0) | 2025.01.07 |