프로그래밍 언어/Python

파이썬 제너레이터와 이터레이터 효율적인 대용량 데이터 처리와 메모리 최적화 - 고급 Python #3

eco7T 2024. 12. 27. 10:16
반응형

이번에는 파이썬의 제너레이터와 이터레이터라는 주제를 다뤄보겠습니다. 고급 파이썬의 내용으로 조금 더 높은 스킬을 얻기를 원하신다면 이 부분 역시 천천히 읽어 보시면 도움이 되실 거예요.

파이썬 제너레이터와 이터레이터
파이썬 제너레이터와 이터레이터



파이썬 제너레이터와 이터레이터

  커스텀 이터레이터 만들기

이터레이터는 단순히 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}")

이런 방식으로 제너레이터를 연결하면, 메모리 사용량을 최소화하면서도 복잡한 데이터 처리 작업을 수행할 수 있습니다.

 

지금까지 제너레이터와 이터레이터의 고급 기법들을 살펴봤습니다. 이 개념들을 잘 활용하면 파이썬 코드의 품질을 한 단계 끌어올릴 수 있다고 봅니다.

반응형