요즘 바빠서 글을 쓸 시간도, 의욕도 없지만, 그래도 시간이 나면 생각날 때마다 내려놓으려고 해요. 이 글도 그 중 하나…
오랜만에 회사에서 일하다가 오늘은 회사에서 꽤 흥미로운 주제로 이야기를 하다가 글을 쓰게 되었습니다. 파이썬이 메인은 아니지만…
Python에는 yield라는 키워드가 있습니다. 다들 아시다시피 함수에서 yield를 사용하면 함수의 반환형은 “Iterator(T) = Generator(T, None, None)”가 됩니다. 사용법은 매우 간단합니다. for 루프를 실행하여 완료하면 해당 반복자가 자동으로 값을 생성하고 수신합니다.
def f1(n):
"""Yield integers from (0,n)."""
i = 0
while i < n:
yield i
i += 1
def scenario0():
for i in f1(2):
print(i)
"""
Result:
0
1
"""
일반 목록에 비해 매우 큰 데이터*(예: 잘 알려진 이야기)를 전달하고 처리할 때 매우 효율적입니다.
그건 그렇고, 그 손익 계산서가 작동할 때 어떻게 작동하는지 생각해 본 적이 있습니까? 잘 알려진 이야기 중 하나는 JavaScript 비동기가 작동하는 방식에 관한 것입니다. 요컨대, Javascript의 경우 마이크로태스크 큐를 사용하여 비동기 호출을 로드하고 FIFO 형식으로 실행했습니다. 불행하게도 파이썬의 yield는 이것과 다릅니다. 다시 말해서,
- next(g)가 호출된 후에만
- 실행 컨텍스트 G 마지막으로 생성기가 값 생성을 시작합니다.
- 발전기가 수율을 만났을 때,
- 이 시점에서 실행 컨텍스트는 최상위 호출 스택으로 돌아가서 반환 값을 받고 나머지 명령을 실행합니다.
이 구조가 작동합니다.
이것이 어떻게 작동하는지 이해하는 데 도움이 되는 좋은 이미지가 있습니다.

그러나 이것이 모든 질문을 해결하는 것은 아닙니다. 실행 컨텍스트가 왔다가 사라지는 동안 우리는 필연적으로 모든 기능이 종료될 것으로 예상합니다. 우리가 알고 있는 모든 프로그램이 그런 식으로 작동하기 때문입니다. 수익에 비해 정말 사실입니까? 다음 코드로 몇 가지 실험을 더 실행했습니다.
def f1(n):
"""Yield integers from (0,n)."""
i = 0
while i < n:
print('YieldStarts')
yield i
print('YieldEnds')
i += 1
print('Complete')
def scenario1():
y = f1(2)
print(y)
print('next1Before')
print(next(y))
print('next1After')
print('next2Before')
print(next(y))
print('next2After')
def scenario2():
y = f1(2)
print(y)
print('next1Before')
print(next(y))
print('next1After')
print('next2Before')
print(next(y))
print('next2After')
print('next3Before')
print(next(y))
print('next3After')
def scenario3():
y = f1(2)
print(y)
print('next1Before')
print(next(y))
print('next1After')
print('next2Before')
print(next(y))
print('next2After')
y.close()
def scenario4():
y = f1(2)
print(y)
print('next1Before')
print(next(y))
print('next1After')
print('next2Before')
print(next(y))
print('next2After')
try:
next(y)
except StopIteration:
pass
결과는 꽤 흥미롭습니다.
- 시나리오 1의 경우: (…, YieldStarts, …., YieldEnds, …, YieldStarts, next2After)이고 프로그램이 종료됩니다. ⇒ 기능 y가 종료되지 않습니다!!
- 시나리오 2의 경우: (…, YieldStarts, …., YieldEnds, …, YieldStarts, …, YieldEnds) y가 끝까지 실행되지만 StopIteration 예외가 발생합니다. 이것은 의도적이며 적절하게 처리되어야 합니다.
- 시나리오 3의 경우: 시나리오 1과 동일합니다.
- 시나리오 4: (…, YieldStarts, …., YieldEnds, …, YieldStarts, …, YieldEnds) y와 프로그램이 모두 끝까지 실행됩니다. 이것은 일반적으로 예상되는 프로그램 실행 순서입니다.
즉, yield를 사용하는 생성기를 사용하는 경우 마지막 “지연된” 논리를 처리해야 합니다. 뒤집힐 것 같으면 트러블에 딱 좋은 구조다. 토스가 발생하는 구조라면 더 문제가 될 수 있습니다.
사담, 상당히 흥미로운 점은 이러한 수익을 중첩하여 연결된 형태로 prep~body~cleansing을 설계할 수 있다는 것입니다. 이와 같은 것이 일반적으로 UI를 통해 수행되는지 궁금하지만 특수 효과로 받아 들여도 괜찮을 것 같습니다.
사담 2) 노션에서 티스토리로 글 옮기는거 너무 부끄럽다. 요즘 Github.io 같은걸로 갈아타야 하나 고민중이네요


