들어가기 전
- 이 글은 이펙티브 파이썬을 공부하며 작성한 글입니다
- 코드 : https://github.com/chickenchickenlove/effective-python/tree/master/item33
요약
- yield from을 사용하면 여러 제네레이터를 합성해서 하나의 제네레이터를 만들 수 있음.
- 직접 내포된 제네레이터를 이터레이션하면서 각 제네레이터의 출력을 내보내는 것보다 yield from을 사용하는 것이 성능 면에서 더 좋음.
yield from
def get_generator():
k = list(range(10))
for v in k:
yield v
def generator():
for v in ["a", "b", "c"]:
yield v
yield from [1, 2, 3]
yield from (4, 5, 6)
yield from range(7, 11)
yield from get_generator()
gt = generator()
while (n := next(gt)) is not None:
print(n)
- yield from 키워드는 다음처럼 사용할 수 있다.
- yield from 키워드는 제네레이터 내에서 다른 제네레이터(자식 제네레이터)에 값의 생성을 위임하고 싶을 때 사용한다.
- 제네레이터는 내부적으로 gi_yieldfrom이라는 attribute를 가지고 있고, gi_yieldfrom이 None이 아닐 때 gi_yieldfrom에 명시된 제네레이터에게 값의 생성을 요청한다.
- next(gt) → next(gt.gi_yield_from)이 호출되는 식으로 Deligation 된다.
- gi_yield_from 제네레이터에서 StopIteration 에러가 발생하면, gi_yield_from은 None이 되고, next(gt) 호출 시 다음 코드 블록으로 넘어가게 된다.
- gi_yieldfrom에 제네레이터가 설정되어있는 동안, next(gt)를 호출했을 때 제어권은 모두 gi_yieldfrom이 가지고 있다. gi_yieldfrom에서 StopIteration이 발생해서 gi_yieldfrom이 None이 되는 순간, 그 때 부모 제네레이터가 제어권을 가진다.
Item 33. yield from을 사용해 여러 제네레이터를 합성하라.
하나의 제네레이터 함수에 여러 제네레이터를 포함하는 경우가 있다면 yield from을 사용할 것을 권장한다. 그 이유는 아래 코드를 보면 알 수 있다.
move(period, speed):
for _ in range(period):
yield speed
def pause(delay):
for _ in range(delay):
yield 0
def animate():
for delta in move(4, 5.0):
yield delta
for delta in pause(3):
yield delta
for delta in move(2, 3.0):
yield delta
for delta in animate():
print(f'Delta: {delta:.1f}')
여기서 animate() 함수의 단점을 살펴보자.
- animate() 함수에서는 3개의 제네레이터가 존재하는데 각각 이터레이팅을 해야한다.
- 단점
- 여러 번 for 문을 사용했음.
- 변수 delta도 여러 번 선언되었다.
단점 중 두번째 부분이 가장 치명적이다. 변수 delta가 여러 번 선언되었고, 불필요하게 delta가 무엇인지에 대해서 인지해야한다. 서로 다른 변수가 선언되어도 문제다. 예를 들어 delta, diff, multifly 같은 변수가 각각 선언된 경우, 각 변수의 이름을 보고 컨텍스트를 계속 파악해야한다. 결론적으로 제네레이터 안에서 여러 제네레이터를 사용하게 되면 문법적으로 잡음이 존재해서 가독성이 떨어진다.
def move(period, speed):
for _ in range(period):
yield speed
def pause(delay):
for _ in range(delay):
yield 0
def animate():
yield from move(4, 5.0)
yield from pause(3)
yield from move(2, 3.0)
for delta in animate():
print(f'Delta: {delta:.1f}')
위 문제의 개선을 위해서 제네레이터 내에서 여러 제네레이터가 내포된다면, yield from을 이용해서 단점을 보완할 수 있다.
- 여러 번 for 문을 사용했음. → 사용하지 않도록 개선
- 변수 delta도 여러 번 선언되었다. → 선언하지 않도록 개선
'프로그래밍 언어 > 파이썬' 카테고리의 다른 글
Effective Python Item 35. 제네레이터 안에서 throw로 상태를 변화시키지 마라. (0) | 2024.01.21 |
---|---|
Effective Python Item 34. send로 제네레이터에 데이터를 주입하지 마라. (0) | 2024.01.21 |
Effective Python Item 32. 긴 리스트 컴프리헨션보다는 제네레이터 식을 사용하라. (0) | 2024.01.20 |
Effective Python Item 30. 리스트를 반환하기보다는 제너레이터를 사용하라 (0) | 2024.01.15 |
Effective Python Item 29. 대입식을 사용해 컴프리헨션 안에서 반복작업을 피해라. (0) | 2024.01.14 |