파이썬 코루틴 : 파이썬 비동기의 시작


파이썬 코루틴 (coroutine)은 일반적인 함수와 유사하지만, 실행 중에 중지되고 재개될 수 있는 특별한 종류의 서브루틴(subroutine)입니다.

코루틴제너레이터와 마찬가지로 실행의 흐름을 일시 중지하고 다시 시작할 수 있으며, 이는 비동기* 프로그래밍과 관련하여 많이 사용됩니다.

이번 포스트에서는 비동기 프로그래밍을 시작하기 위해 알아야하는 기본 개념. 코루틴에 대해서 정리해보았습니다.

비동기란?

비동기적인 작업은 여러 작업이 동시에 진행되어 한 작업의 완료를 기다리지 않고 다음 작업을 수행하는 것입니다.

제너레이터란?

코루틴을 이해하기 위해서는 제너레이터의 기본 원리에 대한 이해가 필요합니다.

제너레이터는 yield 키워드를 사용하여 값을 생산하고 호출자에게 반환합니다.

호출자가 다시 제너레이터를 호출하면, 함수의 상태는 이전 호출에서 중단된 지점부터 다시 시작됩니다.

이 특징을 활용하여 비동기 동작을 모방할 수 있습니다.

파이썬 코루틴 특징

일시 중지 및 재개 가능

코루틴은 실행 중에 일시 중지되고 나중에 재개될 수 있습니다.

이를 통해 여러 작업 간에 실행 흐름을 전환할 수 있습니다.

상태 유지

코루틴은 호출 사이에 상태를 유지할 수 있습니다.

함수의 지역 변수는 호출이 끝나면 소멸하지만, 코루틴은 일시 중지된 상태에서 지역 변수의 상태를 유지합니다.

제너레이터와 유사

코루틴은 제너레이터와 비슷한 문법을 사용합니다.

yield 키워드를 사용하여 일시 중지 및 값을 전달할 수 있습니다.

비동기 프로그래밍

코루틴은 비동기 코드를 작성하는 데에 유용하게 활용됩니다.

async/await 구문을 통해 비동기 코루틴을 작성하여 비동기적인 작업을 쉽게 다룰 수 있습니다.

파이썬 코루틴 (Coroutine) 시작

코루틴은 제너레이터의 확장으로, 호출자와 양방향 통신이 가능합니다.

next() 함수는 제너레이터 또는 코루틴의 실행을 다음 yield 문으로 진행시키는 데 사용됩니다.

# 코루틴 함수
def simple_coroutine():
    print("코루틴 시작")
    received_value = yield  # 호출자로부터 값을 받음
    print(f"호출자로부터 받은 값: {received_value}")
    print("코루틴 종료")

# 코루틴 생성
coroutine = simple_coroutine()

# 코루틴 시작 : yield문까지 실행하고 received_value를 초기화
next(coroutine)

코루틴 시작

next(coroutine)을 호출하면 코루틴이 첫 번째 yield 문을 만날 때까지 실행이 시작됩니다.

더 이상 yield 문이 없으면 StopIteration 예외가 발생합니다.

# 코루틴으로 값 전달
coroutine.send("안녕하세요!")  # received_value에 "안녕하세요!" 문자열 할당

호출자로부터 받은 값: 안녕하세요!
코루틴 종료
Traceback (most recent call last):
  File "c:\Users\Leeyh\Desktop\Project\Python_practice\class_method\17.coroutine.begin.py", line 14, 
in <module>
    coroutine.send("안녕하세요!")
StopIteration

send() 메서드는 코루틴이나 제너레이터가 yield 문에서 일시 중지된 상태에서 데이터를 다시 보낼 때 사용됩니다.

send()를 사용하면 현재 위치에서 다음 yield 문으로 진행됩니다.

다음 yield 문이 없으면 StopIteration 예외가 발생합니다.

코루틴에 next()를 호출하기 전에 send()를 사용한다면?

next()를 호출하기 전에 send()를 사용하면 TypeError가 발생합니다.

코루틴이 처음 시작되고 아직 next()로 실행되지 않은 상태에서 send()를 호출하면, 처음의 yield 표현식이 실행되기 이전이므로 값을 받을 위치가 없어서 오류가 발생합니다.

def simple_coroutine():
    received_value = yield
    print(f"Received value: {received_value}")
    
coroutine = simple_coroutine()
coroutine.send("Hello")  # TypeError 발생

yield, send : 데이터를 주고받기

send 메소드로 코루틴 함수 내로 다음과 같이 데이터를 줄 수 있습니다.

def progress_coroutine(x):
    print('>>> progress started : {}'.format(x))
    progress = yield
    print('>>> progress : {}%'.format(progress))
    add = yield 
    progress += add
    print('>>> progress : {}%'.format(progress))
    add = yield 
    progress += add
    print('>>> progress : {}%'.format(progress))
    add = yield 
    progress += add
    print('>>> progress : {}%'.format(progress))


coroutine = progress_coroutine(0)  # 코루틴 객체를 생성
next(coroutine)  # >>> progress started : 0
coroutine.send(10)  # >>> progress : 10%
coroutine.send(10)  # >>> progress : 20%
coroutine.send(10)  # >>> progress : 30%

위의 코루틴 함수는 yield가 메인루틴에 값을 반환한다는 특성을 사용하여 아래와 같이 작성할 수도 있습니다.

def progress_coroutine(x):
    print('>>> progress started : {}'.format(x))
    progress = yield
    add = yield '>>> progress : {}%'.format(progress)
    progress += add
    add = yield '>>> progress : {}%'.format(progress)
    progress += add
    add = yield '>>> progress : {}%'.format(progress)
    progress += add
    add = yield '>>> progress : {}%'.format(progress)



coroutine = progress_coroutine(0)  # 코루틴 객체를 생성
next(coroutine)  # >>> progress started : 0(첫번째 yield문까지 실행하고 progress를 초기화함)
print(coroutine.send(10))  # >>> progress : 10%
print(coroutine.send(10))  # >>> progress : 20%
print(coroutine.send(10))  # >>> progress : 30%

이번 포스트에서는 코루틴에 대해서 정리해보았습니다. 비동기 프로그래밍을 위한 async, await, future 객체들을 다음에 정리해보겠습니다.

참고할 만한 글

Leave a Comment

목차