프로그래밍백엔드 개발자

파이썬에서 이터레이터와 제너레이터, 그리고 yield 구문이란 무엇이며, 이들이 어떻게 연결되어 있고, 왜 yield가 대량 데이터 처리에 있어 중요한가?

Hintsage AI 어시스턴트로 면접 통과

답변.

이터레이터와 제너레이터는 파이썬에서 시퀀스를 효율적으로 처리하기 위한 기반이다. 역사적으로 파이썬은 데이터 스트림 작업을 간소화하고 많은 컬렉션을 메모리에 과도하게 저장하는 것을 피하려고 했다. 우선 __iter____next__ 프로토콜을 통해 이터레이터 지원이 등장하였고, 그 이후 yield 기반으로 가장 간단한 이터레이터를 생성할 수 있는 제너레이터가 도입되었다.

문제: 대량의 데이터를 처리해야 하는 경우가 많은데(예: 파일 또는 데이터베이스에서 스트리밍) 모든 데이터를 한 번에 메모리에 로드하면 편리하고 효율적으로 작업할 수 없다. 일반 함수는 모든 결과를 한 번에 반환하고, 클래스 내에서 자체 이터레이터를 만드는 것은 간단한 경우에 지나치게 번거로울 수 있다.

해결책: yield 메커니즘을 사용하면 데이터의 "게으른" 생성을 조직할 수 있다. 제너레이터 함수는 리스트나 다른 컬렉션을 반환하는 것이 아니라, 필요할 때마다 값을 계산하는 이터레이터인 제너레이터 객체를 반환한다.

코드 예:

# 간단한 제너레이터 def countdown(n): while n > 0: yield n n -= 1 for i in countdown(3): print(i) # 3, 2, 1

주요 특징:

  • 메모리 절약: 데이터는 요청에 따라 생성되며 미리 생성되지 않는다.
  • 간단한 구문: yield는 몇 줄의 코드로 완전한 이터레이터를 구현한다.
  • 실행 제어: 제너레이터는 호출 간 상태를 저장한다.

함정이 있는 질문.

하나의 함수에서 return과 yield를 사용할 수 있습니까?

네, 하지만 제너레이터 내에서 return은 이터레이션을 종료시키고(StopIteration을 발생시킴), yield는 원하는 만큼 사용할 수 있다.

def example(): yield 1 return # StopIteration

완료된 후 제너레이터를 "다시 시작할" 수 없는 이유는 무엇입니까?

이터레이션이 끝난 후(StopIteration) 제너레이터는 다시 시작할 수 없으며, 새로 생성해야 한다.

gen = countdown(2) list(gen) # [2, 1] list(gen) # [] (제너레이터는 이미 소모됨)

제너레이터와 이터레이터의 차이는 무엇입니까?

제너레이터는 이터레이터의 특별한 경우로, iternext 메소드를 가진 객체는 모두 이터레이터이며, 제너레이터는 yield를 통해 함수로 생성된다.

일반적인 실수 및 안티 패턴

  • 제너레이터가 "일회성"이라는 것을 잊는다: 소모되면 반복 사용할 수 없다.
  • 제너레이터 함수 내에서 return과 yield의 작동을 혼동한다.
  • 생성된 결과를 리스트에 저장하여 게으른 계산의 의미를 잃는다.

실제 사례

부정적 케이스

개발자가 분석을 위해 list(fd)를 통해 파일에서 백만 줄을 메모리에 로드하는 함수를 작성했다. 이것은 서버에서 메모리 부족을 초래했다.

장점:

  • 모든 줄에 대한 빠른 접근 가능.

단점:

  • 메모리 소모가 많음.
  • 메모리 부족으로 인한 오류 발생 가능.

긍정적 케이스

제너레이터를 이용한 파일의 한 줄씩 읽기 및 yield를 사용한 데이터 온디맨드 분석.

장점:

  • 메모리 사용 최소화.
  • 어떤 크기의 파일로도 작업 가능.

단점:

  • 추가 저장 없이는 이미 읽은 데이터에 접근할 수 없음.