먼저 한 가지 방법을 찾아 보자. yield from g
동등한 설명 은 모든 것에 대한 for v in g: yield v
정의 를 시작하지도 않습니다yield from
. 왜냐하면, 모든 yield from
것이 for
루프를 확장하는 것이라면 yield from
언어에 추가하는 것을 보증하지 않으며 파이썬 2.x에서 구현되는 새로운 기능을 완전히 배제 하지 않습니다 .
어떤 yield from
일은 그것이 호출 서브 발전기 사이에 투명 양방향 연결을 설정합니다 :
( 우리가 TCP에 관해 이야기했다면, yield from g
"이제 클라이언트 소켓을 일시적으로 연결 해제했다가 다른 서버 소켓에 다시 연결하십시오"를 의미 할 수 있습니다. )
BTW, 데이터를 생성기로 전송하는 것이 무엇을 의미 하는지 확실하지 않은 경우 모든 것을 삭제하고 코 루틴 에 대해 먼저 읽어야 합니다. 매우 유용 하지만 ( 서브 루틴 과 대조적 이지만) 불행히도 Python에서는 잘 알려져 있지 않습니다. Coroutines에 관한 Dave Beazley의 Curious Course 는 훌륭한 출발입니다. 빠른 입문서를 위해 슬라이드 24-33 을 읽으십시오 .
에서 산출량을 사용하여 발전기에서 데이터 읽기
def reader():
"""A generator that fakes a read from a file, socket, etc."""
for i in range(4):
yield '<< %s' % i
def reader_wrapper(g):
# Manually iterate over data produced by reader
for v in g:
yield v
wrap = reader_wrapper(reader())
for i in wrap:
print(i)
# Result
<< 0
<< 1
<< 2
<< 3
수동으로 반복하는 대신 reader()
할 수 있습니다 yield from
.
def reader_wrapper(g):
yield from g
그것은 작동하고 한 줄의 코드를 제거했습니다. 그리고 아마도 의도가 조금 더 명확하거나 아닙니다. 그러나 인생은 변하지 않습니다.
-Part 1의 yield를 사용하여 생성기 (코 루틴)에 데이터 보내기
이제 더 흥미로운 것을 해봅시다. writer
전송 된 데이터를 받아들이고 소켓, fd 등에 쓰는 코 루틴을 만들어 봅시다 .
def writer():
"""A coroutine that writes data *sent* to it to fd, socket, etc."""
while True:
w = (yield)
print('>> ', w)
이제 문제는 어떻게 래퍼 함수의 핸들은 래퍼로 전송되는 모든 데이터가되고 그래서, 작가로 데이터를 전송해야한다 투명하게 받는 보내 writer()
?
def writer_wrapper(coro):
# TBD
pass
w = writer()
wrap = writer_wrapper(w)
wrap.send(None) # "prime" the coroutine
for i in range(4):
wrap.send(i)
# Expected result
>> 0
>> 1
>> 2
>> 3
랩퍼 는 전송 된 데이터를 명백하게 받아 들여야StopIteration
하며 for 루프가 소진 될 때도 처리해야합니다 . 분명히 그냥 for x in coro: yield x
하지 않을 것입니다. 다음은 작동하는 버전입니다.
def writer_wrapper(coro):
coro.send(None) # prime the coro
while True:
try:
x = (yield) # Capture the value that's sent
coro.send(x) # and pass it to the writer
except StopIteration:
pass
또는 우리는 이것을 할 수 있습니다.
def writer_wrapper(coro):
yield from coro
그것은 6 줄의 코드를 절약하고 훨씬 더 읽기 쉽고 작동합니다. 마법!
-Part 2-예외 처리에서 제너레이터 수율로 데이터 전송
좀 더 복잡하게합시다. 작가가 예외를 처리해야하는 경우 어떻게해야합니까? writer
핸들 a를 말하고 핸들 a를 만나면 SpamException
인쇄 한다고 가정 해 봅시다 ***
.
class SpamException(Exception):
pass
def writer():
while True:
try:
w = (yield)
except SpamException:
print('***')
else:
print('>> ', w)
변경하지 않으면 writer_wrapper
어떻게됩니까? 작동합니까? 해보자
# writer_wrapper same as above
w = writer()
wrap = writer_wrapper(w)
wrap.send(None) # "prime" the coroutine
for i in [0, 1, 2, 'spam', 4]:
if i == 'spam':
wrap.throw(SpamException)
else:
wrap.send(i)
# Expected Result
>> 0
>> 1
>> 2
***
>> 4
# Actual Result
>> 0
>> 1
>> 2
Traceback (most recent call last):
... redacted ...
File ... in writer_wrapper
x = (yield)
__main__.SpamException
음, x = (yield)
그냥 예외를 제기하고 모든 것이 중단 되기 때문에 작동하지 않습니다 . 작동 시키지만 수동으로 예외를 처리하고 예외를 보내거나 하위 생성자 ( writer
)에 던지도록합시다.
def writer_wrapper(coro):
"""Works. Manually catches exceptions and throws them"""
coro.send(None) # prime the coro
while True:
try:
try:
x = (yield)
except Exception as e: # This catches the SpamException
coro.throw(e)
else:
coro.send(x)
except StopIteration:
pass
작동합니다.
# Result
>> 0
>> 1
>> 2
***
>> 4
그러나 이것도 마찬가지입니다!
def writer_wrapper(coro):
yield from coro
yield from
투명 핸들 값을 전송하거나 생성 부에 값을 던지고.
그래도 여전히 모든 코너 케이스를 다루지는 않습니다. 외부 발전기가 닫히면 어떻게됩니까? 하위 생성기가 값을 반환하는 경우 (예, Python 3.3 이상에서는 생성자가 값을 반환 할 수 있음) 반환 값을 어떻게 전파해야합니까? 그것은 yield from
모든 코너 케이스를 투명하게 처리하는 것이 정말 인상적 입니다.yield from
마술처럼 작동하고 모든 경우를 처리합니다.
개인적으로 yield from
는 양방향 선택이 분명하지 않기 때문에 키워드 선택이 잘못되었다고 생각 합니다. 제안 된 다른 키워드가있었습니다 (예 : delegate
언어에 새 키워드를 추가하는 것이 기존 키워드를 결합하는 것보다 훨씬 어렵 기 때문에 거부되었습니다).
요약하면 다음과 yield from
같이 생각하는 것이 가장 좋습니다transparent two way channel
발신자 서브 발전기 사이.
참고 문헌 :
- PEP 380- 하위 생성기에 위임하기위한 구문 (유잉) [v3.3, 2009-02-13]
- PEP 342- 향상된 발전기 (GvR, Eby)를 통한 코 루틴 [v2.5, 2005-05-10]