본문 바로가기
Computer & Program/python

[python] Generator(발생자)

by TDRemon 2024. 3. 4.
반응형

안녕하세요. TDR입니다.

오늘은 python에서 generator를 어떻게 쓰는지 간략히 정리해보겠습니다.

Generator를 이해하기 위해서는 저번에 했던 iterator를 이해하고 있어야 합니다. 왜냐하면 generator는 iterator를 생성해주는 함수이기 때문입니다. (아래 iterator 링크 참고)

 

[python] Iterator(반복자) - 01

안녕하세요. TDR입니다. 오늘은 python의 Iterator(반복자)에 대해서 간략히 정리해 보겠습니다. Iterator는 python 외에도 왠만한 언어에는 모두 존재하는 개념으로 " 반복 가능한 객체로 "를 뜻합니다.

tdremon.tistory.com

 

[python] Iterator(반복자) - 02

안녕하세요. TDR입니다. 저번 시간에 이어서 iterator에 대해서 정리해보겠습니다. https://tdremon.tistory.com/entry/python-Iterator%EB%B0%98%EB%B3%B5%EC%9E%90 iterator의 특징 중에 하나가 unpacking(언패킹)도 가능하다

tdremon.tistory.com

def get_numbers():
  yield 0
  yield 3
  yield 1
  yield 2

gn = get_numbers()
print(gn)  # <generator object get_numbers at 0x7f975f3d2570>
print(dir(gn))
## Result ##
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', 
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', 
'__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', 
'__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 
'throw']

for num in gn:
  print(num)
## Result ##
0
3
1
2

a, b, c, d = get_numbers()
print(a, b, c, d)  # 0 3 1 2

get_numbers() 함수를 보면 별다른 내용 없이 yield라는 키워드에 숫자가 있습니다. (하물며 return 조차 없음)

그리고 밑에서 gn 변수에 함수를 생성해서 출력을 해보니 generator object라고 나옵니다. dir()을 통해 확인해보니 __iter__, __next__가 존재합니다. 즉, iterator로서 동작이 가능하다는 말이 됩니다.

그래서 아래 for문에서 iterator와 같이 출력을 해보았더니 yield를 선언했던 순서대로 숫자가 나옵니다. 혹시나 하여 unpacking을 해보았는데 정상적으로 값이 나오는 것을 알 수 있습니다. 즉, yield를 선언해 줌으로서 함수를 iterator로 만들어 준다는 것을 알 수 있습니다. (그래서 이름이 generator인 듯) 예시에서는 별도로 기입하진 않았는데 next() 등을 통해서 끝까지 호출하면 당연히 StopIteration이 발생합니다.

위에서 yield를 이용한 generator 함수는 return이 없다고 했는데, 만약 return을 넣으면 어떻게 될까요? 그럴경우 StopIteration이 발생하게 됩니다. generator 함수는 next()로 요청한 만큼 yield로 지정한 값을 반환해 주고 해당 상태가 유지됩니다. 그러다 next()를 호출하면 다음 yield 값을 넘겨주고 다 넘겨준 후에 next()를 호출하면 StopIteration을 반환 합니다.

보면 아시겠지만 generator 함수를 만들려면 반환하고 싶은 값들을 yield로 지정해 줘야합니다. 이건 대단히 귀찮고 비효율저인 방법입니다. 그래서 python에서는 아래와 같은 방법을 만들어 놨습니다.

import string

def get_string_digits():
  # string.digits = '0123456789'
  yield from string.digits

for num in get_string_digits():
  print(num, end=' ')  # 0 1 2 3 4 5 6 7 8 9

yield from을 이용해서 하나씩 yield를 쓰는 것을 대체하였습니다. 만약 그냥 yield로 한다면 10번을 써야 합니다. 그리고 반복하려고 하는 반복 가능한 객체의 요소가 10,000개쯤 있다고 하면 더 이상 yield을 하나씩 쓸 수 없을 겁니다. 물론 아래와 같이 쓰는 방법도 있습니다.

import string

def get_string_digits():
  for i in string.digits:
    yield i

어느쪽이 좋다 나쁘다의 문제는 아니지만 가능하면 python에서 제공해주는 해결책을 사용하는 것이 좋을 듯 싶습니다.

반응형

댓글