반응형
소개
Python의 next()와 제너레이터 표현식은 메모리 효율적인 데이터 처리를 위한 강력한 도구입니다. 이번 글에서는 이들의 사용법과 실제 활용 사례를 살펴보겠습니다.
next() 함수 이해하기
기본 사용법
# 간단한 이터레이터 생성
numbers = iter([1, 2, 3])
# next() 사용
first = next(numbers) # 1
second = next(numbers) # 2
third = next(numbers) # 3
# StopIteration 예외 발생
try:
next(numbers) # StopIteration 발생
except StopIteration:
print("더 이상 요소가 없습니다")
next()의 기본값 활용
numbers = iter([1, 2, 3])
# 기본값 설정
value = next(numbers, -1) # 1
value = next(numbers, -1) # 2
value = next(numbers, -1) # 3
value = next(numbers, -1) # -1 (기본값 반환)
장점 | 단점 |
---|---|
메모리 효율적 | 이전 값 재접근 불가 |
지연 평가 지원 | 인덱스 접근 불가 |
기본값 설정 가능 | 길이 계산 어려움 |
직관적인 사용법 | 반복 후 재사용 불가 |
제너레이터 표현식
기본 문법
# 리스트 컴프리헨션
squares_list = [x**2 for x in range(1000000)] # 메모리에 모든 값 저장
# 제너레이터 표현식
squares_gen = (x**2 for x in range(1000000)) # 필요할 때만 값 생성
활용
# 파일 읽기
def read_large_file(file_path):
with open(file_path) as f:
return (line.strip() for line in f)
# 데이터 필터링
numbers = (x for x in range(1000) if x % 2 == 0)
# 데이터 변환
names = ['Alice', 'Bob', 'Charlie']
upper_names = (name.upper() for name in names)
실전 활용
대용량 데이터 처리
def process_large_dataset(data_path):
# 파일에서 데이터 읽기
lines = (line.strip() for line in open(data_path))
# 데이터 필터링 및 변환
processed = (
int(line)
for line in lines
if line.isdigit()
)
# 청크 단위 처리
chunk = []
chunk_size = 1000
for num in processed:
chunk.append(num)
if len(chunk) == chunk_size:
process_chunk(chunk)
chunk = []
메모리 효율적인 데이터 변환
class DataTransformer:
def __init__(self, data):
self.data = data
def transform(self):
# 단계별 변환 파이프라인
return (
self._process(item)
for item in self.data
if self._validate(item)
)
def _validate(self, item):
return bool(item)
def _process(self, item):
return item.upper()
# 사용 예시
transformer = DataTransformer(['a', 'b', 'c'])
result = transformer.transform()
first = next(result) # 'A'
무한 시퀀스 생성
def infinite_sequence():
num = 0
while True:
yield num
num += 1
# 피보나치 수열
def fibonacci_generator():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 사용 예시
numbers = infinite_sequence()
first_five = [next(numbers) for _ in range(5)] # [0, 1, 2, 3, 4]
데이터 파이프라인 구축
def data_pipeline():
# 데이터 소스
raw_data = (x for x in range(1000))
# 필터링
filtered = (
x for x in raw_data
if x % 2 == 0
)
# 변환
transformed = (
x * 2 for x in filtered
)
# 그룹화
groups = (
(i, list(group))
for i, group in itertools.groupby(transformed, key=lambda x: x // 10)
)
return groups
커스텀 이터레이터
class DataStream:
def __init__(self, start=0, step=1):
self.current = start
self.step = step
def __iter__(self):
return self
def __next__(self):
value = self.current
self.current += self.step
return value
# 사용 예시
stream = DataStream(start=5, step=2)
values = [next(stream) for _ in range(5)] # [5, 7, 9, 11, 13]
성능 최적화 팁
메모리 사용량 비교
import sys
# 리스트 컴프리헨션
numbers_list = [x for x in range(1000000)]
print(sys.getsizeof(numbers_list)) # 큰 메모리 사용
# 제너레이터 표현식
numbers_gen = (x for x in range(1000000))
print(sys.getsizeof(numbers_gen)) # 작은 메모리 사용
실행 시간 최적화
from itertools import islice
def optimize_processing():
# 전체 데이터를 한 번에 처리하는 대신
# 청크 단위로 처리
data = (x for x in range(1000000))
chunk_size = 1000
while True:
chunk = list(islice(data, chunk_size))
if not chunk:
break
process_chunk(chunk)
주의사항과 팁
이터레이터 소진 주의
numbers = (x for x in range(5))
list(numbers) # [0, 1, 2, 3, 4]
list(numbers) # [] (이미 소진됨)
# 재사용이 필요한 경우
def get_numbers():
return (x for x in range(5))
list(get_numbers()) # [0, 1, 2, 3, 4]
list(get_numbers()) # [0, 1, 2, 3, 4]
디버깅 팁
def debug_generator(gen):
for value in gen:
print(f"Debug: {value}")
yield value
# 사용 예시
numbers = (x for x in range(5))
debugged = debug_generator(numbers)
list(debugged) # 각 값을 출력하면서 처리
마치며
next()와 제너레이터 표현식은 Python에서 메모리 효율적인 프로그래밍을 가능하게 하는 강력한 도구입니다. 특히 대용량 데이터 처리나 스트리밍 데이터를 다룰 때 매우 유용합니다. 상황에 맞게 적절히 활용하면 프로그램의 성능을 크게 향상시킬 수 있습니다.
'파이썬 > Basic' 카테고리의 다른 글
함수 하나로 여러 가지 처리하기? Python 오버로딩 (2) | 2024.12.27 |
---|---|
자료구조 : deque, Queue, heapq (0) | 2024.12.10 |
순환 참조(Circular Import) 이해하기와 해결 방법 (2) | 2024.12.06 |
파이썬에서 디자인 패턴 적용하기 (7) | 2024.10.24 |
다양한 이미지 확장자와 확장자 변경하기 (5) | 2024.10.23 |