이번에는 파이썬의 제너레이터와 이터레이터라는 주제를 다뤄보겠습니다. 고급 파이썬의 내용으로 조금 더 높은 스킬을 얻기를 원하신다면 이 부분 역시 천천히 읽어 보시면 도움이 되실 거예요.
파이썬 제너레이터와 이터레이터
커스텀 이터레이터 만들기
이터레이터는 단순히 for 루프에서 사용되는 객체가 아닙니다. 이터레이터를 직접 만들어 사용하면, 복잡한 로직을 간결하게 표현할 수 있죠.
커스텀 이터레이터를 만들기 위해서는 __iter__와 __next__ 메서드를 구현해야 합니다. 예를 들어, 피보나치수열을 생성하는 이터레이터를 만들어 볼까요?
class Fibonacci:
def __init__(self, limit):
self.limit = limit
self.a, self.b = 0, 1
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count >= self.limit:
raise StopIteration
result = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return result
# 사용 예
for num in Fibonacci(10):
print(num)
이 코드에서 __iter__ 메서드는 이터레이터 객체 자신을 반환합니다. __next__ 메서드는 다음 피보나치 수를 계산하고 반환하죠. 이렇게 만든 이터레이터는 for 루프에서 자연스럽게 사용할 수 있습니다.
커스텀 이터레이터의 장점은 복잡한 로직을 캡슐화할 수 있다는 점입니다. 사용하는 쪽에서는 내부 구현을 알 필요 없이 간단히 순회만 하면 되니까요.
제너레이터 심화 활용
제너레이터는 이터레이터를 만드는 더 쉬운 방법을 제공합니다. yield 키워드를 사용해 함수를 제너레이터로 만들 수 있죠.
제너레이터 표현식을 사용하면 리스트 컴프리헨션과 비슷한 문법으로 제너레이터를 만들 수 있습니다. 다음의 예를 보죠.
# 1부터 100까지의 짝수를 생성하는 제너레이터 표현식
even_numbers = (x for x in range(1, 101) if x % 2 == 0)
이 코드는 1부터 100까지의 짝수를 즉시 생성하지 않고, 필요할 때마다 생성합니다. 메모리 효율이 아주 좋죠.
또 다른 고급 기법으로는 제너레이터 함수 안에서 다른 제너레이터를 호출하는 방법이 있습니다. yield from 구문을 사용합니다.
def subgenerator():
yield 1
yield 2
yield 3
def main_generator():
yield 'A'
yield from subgenerator()
yield 'B'
for item in main_generator():
print(item) # 출력: A 1 2 3 B
이런 방식으로 제너레이터를 조합하면, 복잡한 데이터 처리 파이프라인을 구축할 수 있습니다.
제너레이터의 확장 - 코루틴
코루틴은 제너레이터의 개념을 한 단계 더 발전시킨 것입니다. 제너레이터가 데이터를 생성하는 데 초점을 맞췄다면, 코루틴은 데이터를 주고받을 수 있는 양방향 통신이 가능합니다.
코루틴은 yield 문을 표현식의 오늘 쪽에 사용합니다. 이를 통해 외부에서 값을 보내고 받을 수 있죠. 간단한 예를 들어볼까요?
def averager():
total = 0
count = 0
average = None
while True:
value = yield average
if value is None:
break
total += value
count += 1
average = total / count
# 사용 예
avg = averager()
next(avg) # 코루틴 준비
print(avg.send(10)) # 10.0
print(avg.send(30)) # 20.0
print(avg.send(5)) # 15.0
이 코루틴은 숫자를 받아 평균을 계산합니다. send 메서드를 통해 값을 보내고, 그 결과로 현재까지의 평균값을 받습니다.
코루틴을 사용하면 상태를 가진 함수를 만들 수 있어, 복잡한 상태 기계(state machine)를 구현하는 데 유용합니다.
대용량 데이터 처리 실용 예시
이제 배운 내용을 실제 상황에 적용해 봅시다. 대용량 로그 파일을 처리하는 시나리오를 생각해 볼까요? 파일이 너무 커서 한 번에 메모리에 올릴 수 없다고 가정해 보죠.
def process_logs(filename):
with open(filename, 'r') as f:
for line in f:
# 로그 라인 처리
parsed_data = parse_log_line(line)
yield parsed_data
def parse_log_line(line):
# 실제로는 더 복잡한 파싱 로직이 들어갈 것입니다.
return line.strip().split()
# 사용 예
for log_data in process_logs('huge_log_file.txt'):
# 각 로그 데이터 처리
print(log_data)
이 코드는 대용량 파일을 한 줄씩 읽어 처리합니다. 제너레이터를 사용했기 때문에 파일 전체를 메모리에 올리지 않고도 효율적으로 처리할 수 있죠.
더 나아가, 여러 처리 단계를 거치는 파이프라인을 만들 수도 있습니다.
def filter_errors(logs):
for log in logs:
if 'ERROR' in log:
yield log
def extract_error_codes(error_logs):
for log in error_logs:
yield log[2] # 에러 코드가 세 번째 필드에 있다고 가정
# 사용 예
logs = process_logs('huge_log_file.txt')
error_logs = filter_errors(logs)
error_codes = extract_error_codes(error_logs)
for code in error_codes:
print(f"Error code: {code}")
이런 방식으로 제너레이터를 연결하면, 메모리 사용량을 최소화하면서도 복잡한 데이터 처리 작업을 수행할 수 있습니다.
지금까지 제너레이터와 이터레이터의 고급 기법들을 살펴봤습니다. 이 개념들을 잘 활용하면 파이썬 코드의 품질을 한 단계 끌어올릴 수 있다고 봅니다.
'프로그래밍 언어 > Python' 카테고리의 다른 글
파이썬 Collections 모듈 가이드 Counter부터 deque까지 활용 방법 - 고급 Python #5 (0) | 2025.01.07 |
---|---|
파이썬 리스트(List) 세트(Set) 딕셔너리(Dictionary) 고급 활용법 - 고급 Python #4 (0) | 2025.01.02 |
메타클래스와 데코레이터로 배우는 파이썬 고급 프로그래밍 - 고급 Python #2 (0) | 2024.12.20 |
파이썬 함수형 프로그래밍과 람다 표현식 고계 함수와 실무 예제 - 고급 Python #1 (0) | 2024.12.17 |
파이썬 입문자를 위한 웹 스크래핑, BeautifulSoup, Requests 라이브러리 - Python 기초 #15 (0) | 2024.05.27 |