몇 년 전에 yield 키워드에 대한 글을 쓴 적이 있습니다. 근데 이 글을 쓴 뒤로 잊을만 하면 받은 질문이 있습니다.

그럼 yield from은 뭐에요?

그 질문에 답하는 글을 써보려고 합니다.

보통 이걸 어디서 보냐 하면

사실 Python을 가볍게 쓰면 yield from은 그다지 쓸 일이 없습니다. 가장 많이 조우하게 되는 곳은 asyncio 기반의 코드들이죠.

@asnycio.coroutine
def coro():
    yield from asyncio.sleep(1)
    print('Hello world!')

뭔가 실행할 때 마다 앞에 붙이는데, 안 붙이면 돌아가지 않습니다.

사실 이 시점에선 그냥 비동기 작업에는 모두 붙여야한다고 생각하고 넘겨도 될 것 같습니다만 그렇게 생각하신 분들은 이 글을 찾지 않으셨겠죠.

원래 용도

yield fromPEP-380에 제안되어 Python 3.3에서 처음 소개된 문법입니다. 정말 단순하게 말하자면 yield from은 다음곽 같습니다.

def gen1():
    for x in range(10):
        yield x
    for x in range(5):
        yield x


def gen2():
    yield from range(10)
    yield from range(5)


for a, b in zip(gen1(), gen2()):
    assert a == b

여기서 gen1gen2는 동일한 동작을 합니다. (따라서 아래 루프에서도 에러가 발생하지 않습니다.) generator 여러개를 이어 붙인다던가 하는 상황에 유용하게 쓸 수 있습니다.

반복문을 써서 yield를 일일히 해주는 경우와 yield from을 쓰는 경우의 차이점이 있다면 send로 값을 주고 받는 경우인데, send된 값은 가장 바깥의 yield로 전송됩니다.1

이게 왜 asyncio랑 쓰이죠?

asyncio는 비동기 프로그래밍을 위해 개발되어 Python 3.4에서 추가된 빌트인 모듈입니다. 가장 큰 특징은 손쉬운 비동기 프로그래밍을 위해 코루틴을 사용한다는 점입니다. 코루틴(Coroutine)은 다중진입점을 가집니다. 실행하다가 중간에 끊고 다른 작업을 하다가 다시 원래 위치로 돌아올 수 있죠.

Python에서 코루틴을 구현하려면 Generator를 써야합니다. yield문을 통해 다중진입점을 구현한 것이죠. 이러한 코루틴을 동작시키고, 실행결과를 받아서 사용하려면 yield from을 사용해야만 하는 것이죠.

하지만 Python 3.5 부터는 await 구문이 추가됩니다. 코루틴을 쓰는 목적이라면 await을 쓰는 쪽이 더 편해져서 yield from은 쓰지 않게 되었습니다.2

요약

  1. yield from은 Generator를 넘기는 용도로 사용한다.
  2. 반복문을 이용해 yield한것과 비슷하지만 yield가 아니기 때문에 send시 동작이 다르다.
  3. 비동기 코드 작성에 쓰였었지만 Python 3.5부터는 await에 대체되었다.

말미

이 글을 보고 궁금함이 하나 해소되었지만 "코루틴이 그래서 뭐야?" 같은 다른 궁금함이 여러개 생겨버린 분들을 위한 부가 설명을 담은 글은 다음 기회에 별도의 작성하겠습니다.


  1. 자세한 점은 What's New in Python 3.3의 yield from 소개 단락을 참조

  2. 하지만 대다수의 비동기 라이브러리들은 하위 호환성을 위해 아직도 yield from을 사용합니다.