IT/파이썬

[코딩도장] day27. 파이썬 제너레이터 사용하기 - yield

_하늘여우_ 2020. 9. 21. 22:21

출처 : unsplash

 

■ 들어가기

- 파이썬 코딩 도장 (남재윤/길벗). 을 공부하며 정리하는 블로그


▶ Unit40. 제너레이터 사용하기

     - 40.1 제너레이터와 yield 알아보기

     - 40.2 제너레이터 만들기

     - 40.3 yield from으로 값을 여러 번 바깥으로 전달하기

 

 

0. 들어가기

- 제너레이터는 이터레이터를 생성해주는 함수
- 이터레이터는 클래스에 __iter__, __next__, __getitem__ 메서드를 구현해야 하는 반면
- 제너레이터는 함수 안에서 yield 라는 키워드만 사용하면 됨

 

 

>> 40.1 제너레이터와 yield 알아보기 <<

- 사용법 : yield 값

목적 : yield에 대해 알아본다.
소스 (yield.py) 결과
def number_generator() :
    yield 0
    yield 1
    yield 2

for i in number_generator() :
    print(i)
0
1
2

 

1. 제너레이터 객체가 이터레이터인지 확인하기

- dir 함수로 메서드 목록 확인

- 목록 중 __iter__ 가 있다면 이터레이터 가능 객체

>>> g = number_generator()
>>> g
<generator object number_generator at 0x02A93030>
>>> 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의 __next__ 를 호출하면 이터레이터와 마찬가지로 0, 1, 2가 반환되며 이후 StopIteration 예외 발생

>>> g.__next__()
0
>>> g.__next__()
1
>>> g.__next__()
2
>>> g.__next__()
Traceback (most recent call last):
  File "<pyshell#85>", line 1, in <module>
    g.__next__()
StopIteration

 

2. for와 제너레이터

출처 : 파이썬 코딩 도장 (dojang.io)

- 제너레이터 함수를 통해 제너레이터 객체(이터레이터)가 호출되면 yield에 정의된 값을 반환
- 현재 함수를 잠시 중단하고 함수 바깥의 코드가 실행되도록 한다..... √ 무슨 말인지 모르겠음..;;

 

 

3. yield의 동작 과정 알아보기

- 사용법
변수 = next(제너레이터객체)

목적 : yield 동작 과정을 알아본다.
소스 (yield_next.py) 결과
def number_generator() :
    yield 0
    yield 1
    yield 2

g = number_generator()

a = next(g)
print(a)

b = next(g)
print(b)

c = next(g)
print(c)
0
1
2

 

 

>> 40.2 제너레이터 만들기 <<

- range(횟수)와 같이 동작하는 기능 구현

목적 : 시퀀스 자료형인 range(횟수)와 동일한 기능을 하는 함수 확인
소스 (generator.py) 결과
def number_generator(stop) :
    n = 0   # 숫자는 0부터 시작
    while n < stop :
        yield n
        n += 1

for i in number_generator(3) :
    print(i)
0
1
2

- number_generator 함수의 인자값으로 3을 건넸으므로 0, 1, 2 3번 발생함
- yield가 3번 나오므로 for 반복문도 3번 반복

 

 

1. yield에서 함수 호출하기

소스 (generator_yield_function.py) 결과
def upper_generator(x) :
    for i in x :
        yield i.upper()

fruits = ['apple', 'pear', 'grape', 'pineapple', 'orange']

for i in upper_generator(fruits) :
    print(i)
APPLE
PEAR
GRAPE
PINEAPPLE
ORANGE

- 만약 upper_generator 함수 내 yield 말고 print(i.upper())로 하면 안될까??

>> 출력은 되는데 마지막에 TypeError 발생! 왜??

Traceback (most recent call last):
  File "C:/project/generator_yield_function.py", line 11, in <module>
    for i in upper_generator(fruits) :
TypeError: 'NoneType' object is not iterable

 

 

>> 40.3 yield from으로 값을 여러 번 바깥으로 전달하기 <<

- yield 뒤에 값을 한 개씩 지정 가능하므로 여러 번 바깥으로 전달 필요 시 for/while 반복문 사용
- BUT, yield from 키워드로 반복 출력할 변수만 지정하면 ok!
- 단, 파이썬 3.3버전 이상부터 가능하며 "반복 가능한 객체", "이터레이터", "제너레이터" 객체를 지정
- 사용법
yield from 반복가능한객체
yield from 이터레이터
yield from 제너레이터객체

소스 (generator_yield_from_iterable.py) 결과
def number_generator() :
    x = [1, 2, 3]
    yield from x    # 리트스에 들어있는 요소를 한 개씩 바깥으로 전달

for i in number_generator() :
    print(i)
1
2
3

 

1. yield from에 제너레이터 객체 지정하기

소스 (generator_yieldl_from_generator.py) 결과
def number_generator(stop) :
    n = 0
    while n < stop :
        yield n
        n += 1

def three_generator() :
    yield from number_generator(3)

for i in three_generator() :
    print(i)
0
1
2