일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- SSA
- 카톡
- react
- 채팅
- 중급파이썬
- dict
- TypeScript
- RDS
- flask
- socket io
- async
- Vue
- node
- docker
- merge
- crud
- Props
- 튜플
- MongoDB
- EC2
- AWS
- 파이썬
- NeXT
- git
- wetube
- lambda
- pandas
- Class
- S3
- SAA
- Today
- Total
초보 개발자
9강 제너레이터 함수 본문
[제너레이터에 대한 이해와 제너레이터 함수]
이번에 소개하는 제너레이터는 iterator 객체의 한 종류이다. 때문에 제너레이터를 전달하면서 next함수를 호출하면 값을 하나씩 얻을 수 있다. 제너레이터를 만드는 두가지 방법이 있다.
- 제너레이터 함수(function) 제너레이터를 만들기 위한 함수 정의
- 제너레이터 표현식(expression) 제너레이터를 만들기 위한 식
def gen_num(): # 제너레이터 함수의 정의
print('first number')
yield 1 # yield가 하나라도 들어가면 제너레이터가 됩니다.
print('second number')
yield 2
print('third number')
yield 3
gen = gen_num() # 제너레이터 생성
yield가 하나라도 들어가면 제너레이터 함수가 된다.
순서는 다음과 같다.
1. gen_num이라는 함수가 호출이 되었다.
2. 함수를 보니 yield가 있네? 함수실행 중지! 제너레이터 객체를 하나 생성
3. 함수에 있는 내용을 싹 긁어서 제너레이터 객체 안에 넣는다. print..yield..
4. gen이 그 제너레이터 객체를 참조한다.
gen이 참조하는 이 제너레이터 객체는 iterator객체이기도 하다라고 했다.
이를통해 next라는 함수를 호출할 수가 있다.
next를 호출하면 그제서야 함수가 실행이 된다. 언제까지? yield를 만날 때까지만 실행이 된다.
그럼 print를 하고 yield를 만나는데 1의 값을 가지고 있으니 next를 호출하면 이 1값이 반환이 되는 것이다. 그리고 멈춘다. 똑같이 next를 호출하면 print('second number')가 실행이 되고 yield를 만나는데 yield의 값이 2니까 2를 반환한다.
또 next를 하면 print('third number')가 호출되고 yield의 값이 3이니까 3을 반환한다.
next(gen) # 첫 번째 next 함수 호출
first number
1
그리고 전부 다 호출을 하게 되면 iterator객체 같은경우는 StopIteration 예외가 발생하였다.
마찬가지로 제너레이터 역시 iterator객체 이기 때문에 다음 yield를 찾지 못하면 StopIteraion 예외가 발생한다.
def gen_for():
for i in [1,2,3]:
yield i # for 루프 돌 때마다 매번 yield문을 실행하게 된다.
g = gen_for()
next(g)
1반환
next(g)
2반환
next(g)
3반환
next(g)
StopIteration 예외 발생
이 역시 yield가 있으니까 제너레이터 객체가 만들어지고 함수내용이 전부 제너레이터 객체로 넘어간다.
next함수를 호출하면 그제서야 처음으로 실행을 한다 언제까지?? yield를 만날 때 까지
처음 for 문에 들어가서 yield를 만나고 1을 가지고 있으니 next는 1을 반환할 것이다. 이러한 메커니즘을 가지고 있다.
[제너레이터가 갖는 장점]
def pow(s):
r = [] # 빈 리스트
for i in s:
r.append(i**2)
return r
st = pows([1,2,3,4,5,6,7,8,9])
for i in st:
print(i, end=' ')
1 4 9 16 ... 81
이러한 함수가 있고 이걸 제너레이터 함수로 만들어 봤을 때
def gpows(s):
for i in s:
yield 1**2
st = gpows([1,2,3,4,5,6,7,8,9])
for i in st:
print(i, end=' ')
1 4 9 16 ... 81
언뜻보기에는 비슷하고 결과적으로는 똑같은 값을 출력한다. 근데 자세히 들여다보면 좀 다르다.
제너레이트 함수는 일단 제너레이트 객체를 만들고 함수의 몸통을 집어 넣은 뒤 next호출 할 때 마다 yield값을 던져준다. 언제??? next호출할때마다
for 루프안에 st를 대상으로 루프를 돌리는데 이게 가능한 이유가 뭐냐면?? 제너레이터 객체가 iterator객체 이기에 그렇다.
처음 st의 메모리는
import sys
sys.getsizeof(st)
100
제너레이터 함수를 사용한 메모리는
import sys
sys.getsizeof(st)
64
앞서 작성한 예에서는 메로리 공간의 크기는 리스트의 길이에 비례해서 늘어난다. 하지만 제너레이터를 사용하는 위의 경우에는 리스트의 길이에 상관없이 사용하는 메모리 공간의 크기가 동일하다. 이유는 단순하다. 제너레이터 객체는 반환할 값들을 미리 만들어서 저장해 두지 않기 때문이다. 이건 일단 준비만 해둔상태!! 앞서만든건 미리 만들어 놓은 상태!!
for i in st 에서 어떻게 호출이 된거지? next가 호출되어야 yield가 반환된다고 하지 않았나?? 헷갈리면
2021.12.16 - [Python/윤성우 열혈 파이썬] - 5강 lterable 객체와 lterator 객체
5강 lterable 객체와 lterator 객체
[ iter함수 ] [1,2,3,4]라는 리스트가 있고 여기의 값을 하나씩 꺼내려면 for 루프를 사용하면 될 것이다. 하지만 연속으로 1 2 3 4가 다사용이 되는데 1 2 만 사용하고 좀더 있다가 3 4 를 사용하고 싶을
taehyeki.tistory.com
여기서 정리한 내용을 한번 읽고 와보자 for가 실제로 어떻게 돌아가는지 !!
정리하자면 생성되는 값들을 순서대로 하나씩 가져다 쓰면 되는 상황에서는 이렇듯 제너레이터를 기반으로 코드를 작성하는 것이 합리적이다. 참고로 앞서 소개했던 map과 filter도 사실은 제너레이터 함수이다. 즉 map과 filter 함수가 반환하는 것은 iterator 객체이자 제너레이터 객체이다. 이렇듯 제너레이터의 존재를 알게 되어서 우리는 map과 filter 함수가 갖는 장점을 추가로 알게 되었다.
def gen_for():
a = [1,2,3]
for i in a:
yield i # for 루프 돌 때마다 매번 yield문을 실행하게 된다.
def gen_for()
a = [1,2,3]
yield from a
이렇게 표현할 수도 있다.
'Python > 윤성우 열혈 파이썬' 카테고리의 다른 글
11강 튜플의 패킹과 언패킹 (0) | 2021.12.22 |
---|---|
10강 제너레이터 표현식 (0) | 2021.12.16 |
8강 map과 filter를 대신하는 리스트 컴프리헨션 (0) | 2021.12.16 |
7강 map & filter (0) | 2021.12.16 |
6강 객체처럼 다뤄지는 함수 그리고 람다 (0) | 2021.12.16 |