ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [코딩도장] day27. 파이썬 코루틴 - next, send, yield
    IT/파이썬 2020. 9. 21. 23:44

    출처 : unsplash

     

    ■ 들어가기

    - 파이썬 코딩 도장 (남재윤/길벗). 을 공부하며 정리하는 블로그


    ▶ Unit 41. 코루틴 사용하기

         - 41.1 코루틴에 값 보내기

         - 41.2 코루틴 바깥으로 값 전달하기

         - 41.3 코루틴을 종료하고 예외 처리하기

         - 41.4 하위 코루틴의 반환값 가져오기

     

     

    0. 들어가기

    - 일반적으로 함수를 호출한 뒤 함수가 끝나면 현재 코드로 다시 돌아옴
    - 예시 : calc 함수에서 add 함수를 호출한 뒤 다시 calc 함수로 돌아옴

    >>> def add(a, b) :
    c = a + b
    print(c)
    print('add 함수')


    >>> def calc() :
    add(1, 2)
    print('calc 함수')


    >>> calc()
    3
    add 함수
    calc 함수

    - calc 함수와 add 함수와의 관계

      calc 함수 : 메인 루틴(main routine), add 함수 : 서브 루틴(sub routine)
    - 메인 루틴에서 서브 루틴을 호출하면 서브 루틴 수행 후 다시 메인 루틴으로 돌아가며
      이 때 서브 루틴 수행 시간 동안 메인 루틴은 대기함

    - 코루틴(coroutine, cooperative routine) : 서로 협력한다는 의미로, 메인 루틴과 서브 루틴처럼 종속된 관계가 아니라 서로 대등한 관계임
    - 특정 시점에 상대방의 코드를 실행

    출처 : 파이썬 코딩 도장 (dojang.io)

    - 코루틴은 함수가 종료되지 않은 상태에서 메인 루틴의 코드를 실행한 뒤 다시 돌아와서 코루틴의 코드를 실행
    - 코루틴이 종료되지 않았기에 코루틴의 내용도 계속 유지
    - 일반 함수는 호출하면 코드를 한 번만 실행 가능하지만, 코루틴은 코드를 여러 번 실행 가능
    (함수의 코드를 실행하는 지점을 진입점(entry point)라 부르며, 코루틴은 진입점이 여러 개인 함수이다)

     

     

     

    >> 41.1 코루틴에 값 보내기 <<

    - 코루틴은 제너레이터의 특별한 형태
    - 제너레이터는 yield로 값을 발생시켰지만, 코루틴은 yield로 값을 받아 옴
    ( 코루틴에 값을 보내면서 코드 실행 시에는 send 메서드 사용 )
    - 사용법

    코루틴객체.send(값)

    변수 = (yield)

    소스 (coroutine_consumer.py) 결과
    def number_coroutine() :
        while True :    # 코루틴 계속 유지위해 무한 루프 사용
            x = (yield) # 코루틴 바깥에서 값을 받아옴
            print(x)


    co = number_coroutine()
    next(co)    # 코루틴 안의 yield까지 코드 실행

    co.send(1)
    co.send(2)
    co.send(3)
    1
    2
    3

    출처 : 파이썬 코딩 도장 (dojang.io)

    - next 함수로 코루틴의 코드를 최초 실행하고, send 메서드로 코루틴에 값을 보내면서 대기하고 있던 코루틴의 코드를 다시 실행

     

     

    >> 41.2 코루틴 바깥으로 값 전달하기 <<

    - 예제 : 코루틴에 숫자를 보내고, 코루틴은 받은 숫자를 누적해서 바깥에 전달하기

    소스 (coroutine_producer_consumer.py) 결과
    def sum_coroutine() :
        total = 0
        while True :
            x = (yield total)   # 코루틴 외부에서 값을 받으면서 외부로 값을 전달
            total += x

    co = sum_coroutine()
    print(next(co))     # 코루틴 안의 yield까지 코드를 실행하고 코루틴에서 나온 값 출력

    print(co.send(1))   # 코루틴에 숫자 1을 보내고 코루틴에서 나온 값 출력
    print(co.send(2))   # 코루틴에 숫자 2를 보내고 코루틴에서 나온 값 출력
    print(co.send(3))   # 코루틴에 숫자 3을 보내고 코루틴에서 나온 값 출력
    0
    1
    3
    6

    - 외부에서 send가 보낸 값은 함수 내부 x에 저장되며, 코루틴 외부로 보낼 값은 total이 된다.

    - 코루틴 외부에서는 "co = sum_coroutine()" 로 코루틴 객체 생성

    - "next(co)"로 코루틴 안의 코드를 최초로 실행하여 yield까지 코드를 실행하여 print로 반환된 값 출력

    - "co.send"로 숫자 1, 2, 3을 보내고 print로 반환된 값 출력

    - next vs send 차이점??
      next : 코루틴의 코드를 실행하지만 값을 (외부로) 보내지 않을 때 사용
      send : 코루틴의 코드를 실행하고 값을 (외부로) 보낼 경우 사용
    - 제너레이터 vs 코루틴 차이점??
      제너레이터 : next 함수(__next__ 메서드)를 반복 호출하여 값을 얻어내는 방식
      코루틴 : next 함수(__next__ 메서드)를 한 번만 호출한 뒤 send로 값을 주고받는 방식

     

     

     

    >> 41.3 코루틴을 종료하고 예외 처리하기 <<

    - 일반적으로 코루틴은 상태 유지를 위해 "while True : " 를 사용해서 무한 루프로 동작하지만

    - 코루틴을 강제 종료하고 싶다면 close 메서드를 사용!

    - 사용법 : 코루틴객체.close()

    소스 (coroutine_close.py) 결과
    def number_coroutine() :
        while True :
            x = (yield)
            print(x, end=' ')

    co = number_coroutine()
    next(co)

    for i in range(20) :
        co.send(i)

    co.close()
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 

     

     

    1. GeneratorExit 예외 처리하기

    - 코루틴 객체에서 close 메서드 호출하면 코루틴이 종료될 때 GeneratorExit 예외가 발생하며,

    - 이를통해 코루틴의 종료 시점 알 수 있음

    소스 (coroutine_generator_exit.py) 결과
    def number_coroutine() :
        try :
            while True :
                x = (yield)
                print(x, end=' ')
        except GeneratorExit :  # 코루틴 종료 시 GeneratorExit 예외 발생
            print()
            print('코루틴 종료')

    co = number_coroutine()
    next(co)

    for i in range(20) :
        co.send(i)

    co.close()
    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
    코루틴 종료

     

     

    2. 코루틴 안에 예외 발생시키기

    - 코루틴 안에 특정 예외 발생시킬 때는 throw 메서드를 사용

    - throw 메서드에 지정한 에러 메시지는 except as의 변수에 들어감

    - 사용법 : 코루틴객체.throw(예외이름, 에러메시지)

    소스 (coroutine_throw.py) 결과
    def sum_coroutine() :
        try :
            total = 0
            while True :
                x = (yield)
                total += x
        except RuntimeError as e :
            print(e)
            yield total     # 코루틴 바깥으로 값 전달


    co = sum_coroutine()
    next(co)

    for i in range(20) :
        co.send(i)

    print(co.throw(RuntimeError, '예외로 코루틴 끝내기'))
    예외로 코루틴 끝내기
    190

    √ 그런데 이 기능은 언제 쓰는거지??;;;

     

     

    >> 41.4 하위 코루틴의 반환값 가져오기 <<

    - 제너레이터에서 "yield from"을 사용하면 값을 바깥으로 여러 번 전달 (40.3 참고)

    - 다만, 코루틴에서 "yield from"은 해당 코루틴에서 return 으로 반환한 값을 가져온다 (단, 파이썬 3.3버전 이상)

    - 사용법

    변수 = yield from 코루틴()

    소스 (coroutine_yield_from.py) 결과
    def accumulate() :
        total = 0
        while True :
            x = (yield)         # 코루틴 바깥에서 값을 받아옴
            if x is None :      # 받아온 값이 None이면
                return total    # 합계 total을 반환
            total += x

    def sum_coroutine() :
        while True :
            total = yield from accumulate()     # accumulate의 반환값을 가져옴
            print(total)

    co = sum_coroutine()
    next(co)

    for i in range(1, 11) :     # 1 ~ 10까지 반복
        co.send(i)              # 코루틴 accumulate에 숫자를 보냄
    co.send(None)               # 코루틴 accumulate에 None을 보내 숫자 누적 종료

    for i in range(1, 101) :    # 1 ~ 100까지 반복
        co.send(i)              # 코루틴 accumulate에 숫자를 보냄
    co.send(None)               # 코루틴 accumulate에 None을 보내 숫자 누적 종료
    55
    5050

    - sum_coroutine 메서드에서 "while True : "로 무한루프 수행 중이므로,

    - print(total) 수행 수 다시 "yield from accumulate()" 로 accumulate를 실행

     

     

    1. StopIteration 예외 발생시키기

    - 코루틴도 제너레이터이므로 return을 사용하면 StopIteration 예외가 발생

    - 따라서 코루틴에서 "return 값" 은 "raise StopIteration(값)" 처럼 사용 가능하며, "yield from" 으로 값을 가져올 수 있음

    - 사용법 : raise StopIteration(값)

    소스 (coroutine_stopiteration.py) 결과
    def accumulate() :
        total = 0
        while True :
            x = (yield)
            if x is None :
                raise StopIteration(total)
            total += x

    def sum_coroutine() :
        while True :
            total = yield from accumulate()
            print(total)

    co = sum_coroutine()
    next(co)

    for i in range(1, 11) :
        co.send(i)
    co.send(None)

    for i in range(1, 101) :
        co.send(i)
    co.send(None)
    55
    5050

     

    댓글

Designed by Tistory.