8.10. Awaitable 클래스¶
8.10.1. awaitable이란 무엇인가¶
코루틴이 await x를 작성하면, 언어는 x에게 어떻게 대기할지를 묻습니다. 이에 답하는 방법을 아는 모든 객체는 awaitable입니다. 두 가지 종류가 있습니다:
async def함수가 반환하는 코루틴 객체입니다.send_request()를 호출하면 매번 이 중 하나가 생성됩니다 – 함수의 결과가 아니라 코루틴 객체입니다.await send_request()를 작성하는 것이 실제로 본문을 실행하는 일입니다.__await__메서드를 정의한 클래스의 객체입니다.async def는 첫 번째 종류의 축약 표현이고,__await__는 두 번째 종류를 직접 만드는 방법입니다.
애플리케이션 코드는 기본적으로 첫 번째 종류를 사용합니다. 두 번째 종류는 메서드를 호출한 결과가 아니라 객체 자체가 호출자가 await할 대상이 되어야 하는 드문 경우를 위해 존재합니다.
8.10.2. 두 형태의 나란한 비교¶
동일한 로직을 코루틴으로 작성한 것과 awaitable 클래스로 작성한 것입니다:
import asyncio
# async def -- almost always the right choice
async def yield_then_return(value):
await asyncio.sleep_ms(0)
return value
# awaitable class -- equivalent, rarely written
class YieldThenReturn:
def __init__(self, value):
self._value = value
def __await__(self):
yield # let the loop run once
return self._value # value of the ``await`` expression
둘 다 같은 방식으로 await할 수 있습니다:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
async def 형태는 더 짧고, 일반적인 Python처럼 읽히며, 모든 일시 중단 및 재개 관리를 언어에 맡깁니다. 이 예제에서 클래스 형태는 추가로 얻는 것이 없습니다.
8.10.3. 클래스 형태가 제 역할을 하는 경우¶
객체가 호출자가 코루틴을 얻기 위해 호출하는 함수가 아니라, 애플리케이션이 주고받는 무언가가 되어야 할 때는 클래스 형태가 유일한 선택지입니다:
애플리케이션이 생성하여 생산자에게 넘긴 뒤 나중에
await해서 생산된 결과를 가져오는 future와 유사한 값.기존 프리미티브 위에 만들어진 사용자 정의 프리미티브로, 자연스러운 API가
await my_thing.wait()가 아니라await my_thing인 경우입니다.asyncio.Task클래스 자체가 내장된 예시입니다 –await task가 동작하는 이유는Task가__await__를 정의하기 때문입니다.
함수를 호출하고, 코루틴을 얻고, 이를 await한다는 형태에 맞는 모든 것에는 async def가 낫습니다.
8.10.4. 프로토콜 세부사항¶
__await__는 이터레이터를 반환하는 일반 메서드입니다(async def가 아닙니다). 본문에 최소한 하나의 yield를 포함하는 제너레이터로 작성하면 자동으로 이터레이터가 됩니다. 각 yield는 그 객체를 await하는 코루틴을 일시 중단하고 제어권을 이벤트 루프로 돌려줍니다. 제너레이터는 루프가 다음에 태스크를 스케줄링할 때 재개됩니다. 빈 return(또는 끝에 다다르는 것)은 대기를 끝냅니다. return이 생성하는 값이 무엇이든 await 표현식의 값이 됩니다.
실제로 이를 수행하는 드문 클래스는 루프를 직접 구동하기보다 yield 작업을 기존 awaitable에 넘깁니다:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
yield from은 실제 일시 중단을 위해 기반 이벤트의 __await__를 재사용합니다. 대부분의 asyncio 애플리케이션은 두 형태 중 어느 것도 작성할 필요가 없습니다.