[파이썬] Generator 가 뭐예요?

Clueless Coder·2022년 3월 14일
0

뭐예요?

목록 보기
2/4
post-thumbnail
post-custom-banner

Generator

제너레이터가 뭐예요?

제너레이터제너레이터 이터레이터를 리턴하는 함수다(그래서 제너레이터 함수라고도 한다).

일반적인 함수랑 똑같이 생겼는데, 어떤 값을 return 하는 대신 yield 표현을 써서 for 루프나 next() 함수로 한 번에 하나씩 원소를 가져올 수 있는 값의 스트림을 반환하는 제너레이터 이터레이터/객체를 만들어준다.

아주 간단히 말하자면 yield 키워드를 함수에 썼다면 제너레이터 함수고, 제너레이터 이터레이터 객체를 리턴한다. (yield 는 제너레이터 함수를 만들때만 사용된다)

제너레이터라고 하면 일반적으로 제너레이터 함수를 의미하지만, 어떤 경우에는 제너레이터 이터레이터(제너레이터 함수에 의해 생성된 제너레이터 객체를 의미한다). 여기서는 명확히 하기 위해 제너레이터 함수제너레이터 이터레이터/객체라고 구분지어 말하겠다.

그래서 yield 가 들어간 함수를 돌리면 무슨 일이 일어나는데요?

def generate_ints(N):
	for i in range(N):
    	yield i

위는 아주 간단한 제너레이터 함수의 예다.

이 함수는 어떤 하나의 값을 리턴하지 않는다. 대신 yield 라는 애로 인해 이터레이터 프로토콜을 지원하는 제너레이터 객체를 리턴한다. (이터레이터와 이터러블에 대해서는 앞선 포스트에서 구체적으로 설명했으니 여기서는 아는 것을 전제로 하겠다.)

자, 이 함수를 호출했다고 해보자.
제너레이터 객체가 생성된 것을 볼 수 있다. 우리는 이 제너레이터 객체를 for 문에 쓸 수 있다. 우리가 이터러블이나 이터레이터를 for 문에 쓰듯!

for 루프 내부적으로는 무슨 일이 일어났을까? 이터레이터나 이터러블에 일어나는 과정과 같아 보인다!

구체적으로 어떤 일이 일어나는지 살펴보자. __next__() 메서드가 호출될 때마다, for 루프 내부적으로는 제너레이터 함수가 실행된다. 그리고 yield 오른쪽의 객체가 리턴되고, 제너레이터 함수의 실행 상태가 일시 중지되고, 지역 변수가 보존된다. 제너레이터의 __next__() 메서드가 실행되면 함수가 다시 yield 에 도달할 때까지 실행되고 또 다시 오른쪽 객체가 리턴되고 일시 중지된다. 루프는 제너레이터 함수가 더 이상 어떤 객체를 'yielding' 하지 않을 때까지 돌아가고 제너레이터 함수가 끝난다.

제너레이터는 왜 쓰나요?

그냥 편하게 리스트를 만들면 될 것을 왜 굳이 제너레이터를 사용할까?

리스트를 하나 만들면 그 리스트의 모든 원소들이 메모리에 저장될 것이다. 여기서 두 가지 의문을 제기할 수 있겠다.

  • 이 모든 원소들이 동시에 메모리에 존재할 수 있다면 괜찮지만, 그렇지 않다면?
  • 또 만약 얼마나 많은 원소들이 리스트에 들어갈지 알 수 없고 런타임에서만 알 수 있다면 어떨까?

이런 문제를 해소하기 위해 우리는 리스트 대신 제너레이터를 쓸 수 있다. 제너레이터는 메모리에 객체를 '게으르게' 생성하고 로딩할 수 있게 해준다; 한 번에 하나씩 그리고 그런 객체를 얼마만큼이고 '제너레이트(생성)'해준다.

그래도 yield 를 써서 제너레이터 함수를 귀찮게 만들 바에는 그냥 리스트 쓰면 안되나요?

yield 키워드를 써서 제너레이터 함수를 정의하는 대신 아주 간단하게 제너레이터를 만들 수 있다!

list comprehension 과 같은 방식으로 제너레이터 expression 을 만들 수 있다. 차이점은 대괄호를 소괄호로 만들어 줘야 한다는 것이다.

list_comprehension = [ i for i in range(5) ]
generator_expression = ( i for i in range(5) )

만드는 방법은 유사하지만, 결과물은 다르다. list comprehension 문이 모든 객체를 다 가지는(fully populated) 리스트를 만들어줬다면, generator expression은 제너레이터 객체를 리턴할 뿐이다. 그리고 순회할 때만 '게으르게' 원소를 하나씩 리턴해준다.

🤓 조금 더 들여다볼 사람!

ge = (i for i in range(5))

generator expression 에 대해서 모르고 이 표현을 봤다면, 아마 tuple comprehension 이 아닌가 했을 것이다. 하지만 tuple comprehension 은 없다! (이걸 만들어야 한다는 논의가 있다고는 한다.)

튜플로 만들어주고 싶다면 아래처럼 하면된다.

>>> tuple((i for i in range(5))
(0, 1, 2, 3, 4)
post-custom-banner

0개의 댓글