상세 컨텐츠

본문 제목

[Python] Generator란? Generator와 Iterator.

프로그래밍/Python

by jisooo 2019. 8. 25. 18:36

본문

g.__next__()
0
g.__next__()
1
g.__next__()
2
g.__next__()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration
​
def test_generator():
  yield "hello, generator!"

Generator란 값을 생성하는 함수를 지칭한다.

일반적으로 함수는 "return" 키워드를 이용하여 특정값을 생성, 연산하여 반환한다.

Generator와 일반 function의 가장 큰 차이점은

일반 함수는 위처럼 return을 통해 값을 반환하지만, Generator는 "yield"키워드를 통하여 값을 반환한다.

그렇다면 두 키워드를 통해 값을 반환하는 건 똑같은데 무슨 차이점이 있을까?

return 키워드를 이용하여 값을 리턴하면 return 키워드 아래에서 수행하는 코드들은 실행되지 않고 끝나버린다.

그러나 "yield" 키워드를 통해 값을 반환하게 되면 값을 반환한 이후에도 계속

yield 코드 아래에 작성된 코드들을 계속 수행할 수 있다.

 

 

async def get_connection():
  connect = await asyncpg.connect(
            f'postgresql://{USER}:{PASSWORD}@{HOST}/{DB}'
        )
  ...
  return connect    

async def get_connection2():
  connect = await asyncpg.connect(
            f'postgresql://{USER}:{PASSWORD}@{HOST}/{DB}'
        )
  ...
  yield connect
  await connect.close()    
 async def get_session():
    session = aiohttp.ClientSession()
    return session

async def get_session2():
    session = aiohttp.ClientSession()
    yield session
    await session.close()

 

첫번째 코드는 postgreSQL에 연결정보를 설정하여 connect object를 리턴하는 함수이다.

두번째 코드는 aiohttp의 ClientSession을 리턴하는 함수이다.

위 코드는 그냥 connect 객체를 리턴하면 끝나지만,

리턴 하고 난 후, 각각 외부에서 해당 값을 공유하고 사용한 후에 , connect 객체를 닫아주고 싶은 추가적인 수행이 필요할 수도 있다.

각각 다시 작성한 2번째 function들은 각 객체를 yield하고 추가적인 작업을 수행할 수 있는 제너레이터 함수로 작성한 것이다.

이와 같이 값을 전달 후 추가적인 로직을 수행하고 싶을 때 "yield" 키워드를 사용하면 유용하다.

또한, 제너레이터는 아래와 같이 반복문과 함께 쓰일 때도 유용하게 사용될 수 있다.

 

 

def generator_repeat_nums():
    num = 0
    while num <= 10:
        yield num
        num = num + 1


nums = generator_repeat_nums()
 
for x in nums:
    print(x)

함수 generator_repeat_nums()를 보면, num을 생성하고 num이 10 이하가 될 때까지 계속해서 변수 num을 yield한다.

이는 일반적인 function에서는 사용할 수 없는 구조이다.

그냥 값을 return해버리면 위의 예시처럼 추가적인 다른 값을 계속 리턴할 수 없고 한번 리턴하면 코드 수행이 끝나버리기 때문이다.

이와 같이 제너레이터는 마치 iterable처럼 동작시킬 수 있으며, 함수 실행을 잠시 중단하고, 여러개의 값을 전달할 수 있다.

제너레이터는 위 예시처럼 이터레이터를 생성해주는 함수라고 할 수 있다.

참고로 이터레이터는 클래스에 __iter__, __next__ 또는 __getitem__ 메서드를 구현해야 하지만,

제너레티어는 함수 안에서 yield키워드만 사용해주면 된다.

위의 코드 예시에서 제너레이터 함수 밖에서 nums 변수에 제너레이터 함수를 대입하였다.

그렇다면 nums의 타입은 무엇이 될까?

type(nums)
<class 'generator'>

 

타입을 체크해보면 제너레이터 '객체'가 담긴 것을 확인 할 수 있다.

그렇다면 이터레이터와 같이 동작하는 제너레이터도 과연 이터레이터라고 할 수 있는가?

 

 

더 단순한 예시를 통해 이해를 해보자.

def test_gen():
  yield 0
  yield 1
  yield 2

for i in test_gen():
  print(i)

test_gen이라는 제너레이터를 작성하였고, 제너레이터를 for문을 통해 yield되는 값을 찍어보았다.

 

0
1
2

 

결과는 우리의 예상과 같이, 제너레이터에서 yield한 값이 각각 순차적으로 출력되는 것을 확인할 수 있다.

 

 

 

이제, 위 제너레이터를 특정 값에 할당하고, dir함수로 메소드 목록을 출력해보겠다.

g = test_gen()
dir(g)
['__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']

 

신기한 광경을 볼 수 있다!

우선 g의 타입을 검사해보면 역시 generator object인 것을 확인 할 수 있고,

해당 제터레이터 객체의 메소드 목록을 보면

"__iter__, __next__" 메소드가 들어 있는 것을 확인할 수 있다.

그렇다면 이터레이터처럼 동작하는지 __next__ 메소드를 통해 제너레이터의 값을 출력해보자.

 

 

 

순차적으로 yield된 값들을 출력한 뒤, 더이상 yield할 값이 없으면

이터레이터의 동작과 동일하게 "StopIteration" 예외가 발생하는 것을 확인 할 수 있다.


generator와 Iterator

정리하자면 제너레이터는 이터레이터가 맞고, 구현법이 이터레이터보다 훨씬 간단하다는 것을 확인 할 수 있다.

제너레이터 객체를 생성하면 이터레이터에서 구현해야하는 메소드인 __iter__, __next__ 메소드가 구현되어 있다.

이터레이터는 __next__ 메소드를 직접 구현하여 그 안에서 return하여 값을 반환했지만,

제너레이터는 yield에서 반환한 값이 __next__ 메소드의 반환값으로 나온다.

또 이터레이터에서는 값을 return한 후, 더이상 반환할 값이 없으면 StopIteration 예외를 직접 발생시켜야 했지만,

제너레이터에서는 모든 yield문을 수행하고 함수의 끝까지 도달하면 StopIteration 예외를 자동으로 발생시켜준다.

제너레이터는 제너레이터 객체에서 __next__ 메서드를 호출할 때마다 함수 안의 yield까지 코드를 실행하며 yield에서 값을 발생시킨다.

그래서 이름이 값을 발생시킨다는 의미의 generator이다.

 

 

 

 

 

<참고사이트 및 도서>

 

http://masnun.com/2015/11/13/python-generators-coroutines-native-coroutines-and-async-await.html

 

Python: Generators, Coroutines, Native Coroutines and async/await

NOTE: This post discusses features which were mostly introduced with Python 3.4. And the native coroutines and async/await syntax came in Python 3.5. So I recommend you to use Python 3.5 to try the codes. Generators Generators are functions that generates

masnun.com

https://book.naver.com/bookdb/book_detail.nhn?bid=14144026

 

파이썬 코딩 도장

외우기만 해서는 파이썬도 어렵다!프로그래밍은 연습으로 배우는 것!문법을 외우기만 해서는 파이썬도 배우기 어렵다. 프로그래밍은 개념을 습득하고, 개념을 이해한 다음에 자신만의 프로그램을 만들면서 실력을 늘리는 것이 가장 좋다. 이제 막 문법을 배우는 중에 어떻게 프로그램을 만들라는 것인가? 그래서 연습문제와 심사문제는 방금 배운 문법만으로 코드를 작성하는 훈련을 하도록 배려했다.최고의 학습은 반복이고, 스스로 생각하기다파이썬 문법을 학습한 다음에는 반드시

book.naver.com

 

관련글 더보기

댓글 영역