개발자 블로그

[윤성우의 열혈 파이썬 중급편] - 6. generator 함수와 표현식 본문

파이썬/윤성우의 열혈 파이썬 중급편

[윤성우의 열혈 파이썬 중급편] - 6. generator 함수와 표현식

hayongwoon 2022. 4. 27. 18:04

'제너레이터'는 Iterator 객체의 한 종류이다! 따라서 next 함수를 호출하면 값을 하나 씩 얻을 수 있다. 

제너레이터 객체를 만들기 위한 두 가지 방법

  • 제너레이터 함수(function)                제너레이터를 만들기 위한 함수 정의
  • 제너레이터 표현식(expression)        제너레이터를 만들기 위한 식

 

제너레이터 함수

제너레이터 함수를 만들기 위해선 꼭 필요한 녀석이 있다! 바로 yeild라는 것인데, 함수에서 return과 비슷한 역할이라고 생각하면 된다.

우리는 함수 내에 yield가 선언되기만 하면 아 이 함수는 제너레이터 함수구나라고 생각하면 되겠다.

>>> def gen_num():
        print('first num')
        yield 1
        print('second num')
        yield 2
        
>>> gen = gen_num()
>>> next(gen)
first num
1
>>> next(gen)
second num
2
next(gen)
Traceback (most recent call last):
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/code.py", line 90, in runcode
    exec(code, self.locals)
  File "<input>", line 1, in <module>
StopIteration

일반 함수라면 gen_num()이라는 함수를 호출함과 동시에 그 안에 내용들이 실행이 되야하는데, 여기서는 그렇지 않는다. 대신 '제너레이터라는 객체'를 생성하여 반환해준다! 

 생성된 객체를 변수에 참조하여 next()함수로 호출하면 순서대로 호출이 될 때 마다 값이 나오는 것을 확인할 수 있고, iterator 객체와 마찬가지로 마지막 이후에 next함수가 호출이 되면 StopIteration 예외가 발생한다. 이 역시 제너레이터 객체도 iterator 객체이기 때문!

참고로 이렇게 호출이 될 때마다 실행이 되는 흐름, next 함수가 호출이 될 때가지 실행을 미루는 특성을 가리켜 'lazy evaluation'이라 한다! 

 이러한 특성 덕분에 제너레이터 객체는 미리 반환되는 값을 메모리에 저장시키지 않는다! 따라서 사용되는 메모리의 크기가 항상 일정하다! 

정리하면, 생성되는 값들을 순서대로 하나씩 가져다 쓰면 되는 상황에서는 제너레이터를 기반으로 함수를 작성하면 합리적이다! 앞서 블로그에 정리한 map과 filter 함수도 iterator 객체이자 제너레이터 객체이므로 같은 장점을 갖는다! 때문에 상황에 맞춰 합리적인 코드를 짜도록 하자! 

 

추가로 yield from 이라는 문법도 알아보겠다!

이 문법은 파이썬 3.3 이상에서 사용할 수 있다고 한다.

>>> def get_num():
        ns = [0,1,0,1,0,1]
        for i in ns:
            yield i

>>> g = get_num()
>>> next(g)
0
>>> next(g)
1

yield from 사용 예제를 보면

>>> def get_num():
        ns = [0,1,0,1,0,1]
        yield from ns    #ns의 값들을 하나 씩 yield!

>>> g = get_num()
>>> next(g)
0
>>> next(g)
1

for 문을 간단하게 from을 사용하여 한 줄로 줄일 수 있다! 

 

 

제너레이터 표현식

의외로 제너레이터를 만들기 위한 식은 간단하다! 우리가 알고 있는 리스트 컴프리헨션과 매우 비슷하게 생겼다!

  • list_comprehension = [ i for i in range(1, 10) ]      # 리스트 컴프리헨션
  • generator_expression = ( i for i in range(1, 10))   # 제너레이터 표현식

언뜻 보면 튜플 컴프리핸션아닌가 생각을 할 수 있지만 이는 재너레이터 객체를 생성하는 '제너레이터 표현식'이다! 과연 잘 될까 의문이 든다. 그럼 확인해보자!

>>> g = (2 * i for i in range(1, 10))
>>> next(g)
2
>>> next(g)
4
>>> next(g)
6

next 함수를 호출할 때 마다 값이 출력되는 것을 확인할 수 있다. 이렇게 표현식을 알아봤는데 기존에 함수를 불러오는 코드를 만드는 것과 비교할 때 매우 간결하다! 사실, 표현해야할 식이 복잡해진다면 그 식에 대한 장점이 가려지곤 한다. 때문에 이러한 부분도 상황에 따라 사용하여 더 나은 효과를 누려보자!