코루틴(cooperative routine)이란??
여태 함수내에 함수를 사용하면 메인루틴과 서브루틴으로 나뉘어져 서브루틴은 해당 함수를 실행할때만 사용되고 곧바로 종료되었다.
코루틴이란 종료되지않고 서로 대기상태에 머물며 상호작용하는 코드 방식을 의미한다.
- 코루틴은 기본적으로 제너레이터의 형태이다.
- 코드를 실행할땐 send 메서드를 사용한다.
- send 메서드가 보낸값을 (yield)형식으로 받아 변수에 저장한다.
- 상호작용하는 함수 코드들의 실행 지점을 진입점(entry porint)라고 한다.
- while True:를 사용하여 무한반복루프로써 코드가 종료되지않게 한다.
1단계! 코루틴에 값을 보내보기
- 코루틴객체.send(값) : 코루틴에 값을 전달한다
- 변수 = (yield) : 전달 받은 값을 변수에 저장한다.
만들기
def number_coroutine():
while True:
x = (yield)
print(x)
co = number_coroutine()
next(co)
co.send(1)
co.send(2)
co.send(3)
결과
1
2
3
기본셋팅
def number_coroutine():
while True: #무한루프
x = (yield) #(yield)로 값을 받아 x에 할당
print(x) #x를 출력
작동원리 알아보기
1.인스턴스 생성 후 next로 최초실행
co = number_coroutine()
next(co) #co.__next__()도 사용가능
코루틴의 최초실행은 next(코루틴객체)로 실행된다. 최초실행시 진행되는 현황은 아래와같다.
2.next(co)의 결과
while True:
x = (yield)
위의 코드까지만 실행되며 여기서 다음 단계로 넘어간다.
3.co.send(1)로 1을 코루틴에 전달
co.send(1)
4.co.send(1)을 하면 코루틴함수 내부에서 어떤일이 일어나나?
x = (yield) #(yield) 로 co.send(1)의 값인 1을 받아 냈다.즉 x는 1인 상태
print(x)
x = (yield) #여기서 대기
co.send(1)로 코루틴함수에 1을 보내주었고 (yield)로 받고 x에 할당 하였다.
그후 x = (yield) 상태에서 또 대기한다.
5.send 메서드 만큼 반복후 종료한다!
코루틴 최초 실행방법 3가지
- next(코루틴객체)
- 코루틴객체.__next__()
- 코루틴객체.send(None)
2단계 코루틴에 값을 보내고 바깥으로 전달 해보기!
- 변수 = (yield 변수2)
- 변수 = next(코루틴객체)
- 변수 = 코루틴객체.send(값)
(yeild 변수) 를사용해 변수를 바깥으로 전달하기
만들기
def sum_coroutine():
total = 0
while True:
x = (yield total)
total += x
co = sum_coroutine()
print(next(co))
print(co.send(1))
print(co.send(2))
print(co.send(3))
결과
0
1
3
6
기본셋팅
def sum_coroutine():
total = 0 #변수 total의 기본값은 0
while True: #무한루프 생성
x = (yield total) #(yield total)로 값을 받고 x에 할당
total += x #total += x로 send로 보내준만큼 토탈이 증가한다.
작동원리 알아보기
1. 인스턴스 생성후 next(co)로 최초실행
co = sum_coroutine()
print(next(co))
이경우 이전 예제와 다르게 print()함수를 사용하여 출력하였다.
2.next(co)의 결과
total = 0
while True:
x = (yield total) #total을 메인루틴에 전달 (현재 0 인 상태)
최초실행이므로 따로 값이 없기에 total 의 기본값 x = 0 이 된다.(이전예제와 다르게 print()함수도 사용했기에 0을 출력)
3.co.send(1)로 코루틴에 1을 전달
print(co.send(1))
4.co.send(1)을 하면 코루틴함수 내부에서 어떤일이 일어나나?
x = (yield total) #(yield total)의 값은 1 이므로 x의 값은 1이 된다.
total += x #total은 0 이였고 x는 1이므로 결과는 total = 1이 된다
x = (yield total) #total을 메인루프로 전달
5.print(co.send(1))을 출력 후 co.send(2)로 새로운 값을 코루틴에 전달
print(co.send(1)) #결과로 1이출력된다.
print(co.send(2)) #코루틴에 2를 전달
6.co.send(2)을 하면 코루틴함수 내부에선 어떤일이 일어나나?
x = (yield total) #(yeild total)로 2를 받아왔다 즉, x는 2다
total += x #이전 값에서 total은 1이였으므로 +2를 해서 3이된다.
x = (yield total) #현재의 total의 값인 3을 전달한다.
7.print(co.send(2))로 출력 후 나머지 send메서드를 같은방식으로 반복
print(co.send(2)) #3이 출력된다.
print(co.send(3)) #3을 전달한다.
조금 복잡해 보이지만 우리가 알 수 있는것은 코루틴에서 (yield 변수)는 받기만 하는기능이아닌 내보내기 기능도 가능했다.
제너레이터와 코루틴의 차이점
- 제너레이터는 next 함수를 반복 호출하여 값을 얻어내는 방식
- 코루틴은 next 함수를 최초실행시에만 사용하고 send로 값을 주고 받는 방식
값을 보내지 않고 코루틴을 실행하려면?
next만 계송 사용하면 된다. 하지만 이것은 제너레이터와 다른것이 없다.
코루틴 종료해보기
- 코루틴객체.close() - 코루틴을 강제 종료하는 메서드이다.
만들기
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
while True: 는 무한루프이기에 종료를 시켜줘야하지만 사실 파이썬 스크립트가 끝나면 코루틴도 자동종료된다.
아래에서 활용도를 더 찾아보자
GeneratorEix 예외처리하기
코루틴 객체에서 close 메서드를 호출하면 코루틴이 종료될 때 GeneratorExit 예외가 발생한다.(코루틴 종료시점을 확인가능)
만들기
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
코루틴 종료
코루틴 안에서 직접 예외 발생시키기
- 코루틴객체.throw(예외이름, 에러메세지)
코루틴안에서 특정 예뢰를 발생시키는 방법이다.
throw (던지다)라는 말 의미대로 예외를 코루틴 안으로 던지는것이다.
이때 지정한 에러 메세지는 except as 의 변수로 들어간다.
만들기
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
예외를 발생 시키고 예외에 해당하는 문자열을 출력하였다. 그후
yield total로 현재까지 누적된 total값 역시 출력 하였다.
하위 코루틴의 반환값 가져오기
- 변수 = yield from 코루틴()
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
작동원리 알아보기
1.accumulat(): 코드 뜯어보기
def accumulate():
total = 0 # total의 기본값은 0
while True:
x = (yield) # 코루틴 바깥에서 값을 받아옴
if x is None: # 받아온 값이 None이면
return total # 합계 total을 반환
total += x
(yield)로 값을 받아와서 x에 할당 하고 total += x를 반복 언제까지?? 값이 None일 때까지
2.sum_coroutine(): 코드 뜯어보기
def sum_coroutine():
while True:
total = yield from accumulate() # accumulate의 반환값을 가져옴
print(total)
yield from 으로 accumulate()라는 코루틴의 반환값을 가져온후 total에 할당 print()함수로 total을 출력한다.
3. sum_coroutine()의 객체 생성 후 for 반복문으로 값을 전달
계산식은 accumlate()코루틴이지만 값은 sum_coroutine()으로 보내는 코드다 왜그럴까?
yield from accumulate()는 값을 받을땐 받아서 accumlate()에 전달하고
출력할땐 accumulate()의 반환값을 꺼낸다.
4.for 반복문으로 전달하기 값을 전달하기
for i in range(1,11):
co.send(i)
이제 1부터 10까지 모든 정수가 코루틴을통해 total 에 중첩된다
5.co.send(None)으로 종료후 반환받기
if x is None:
return total
이란 코드가 있었다. co.send(None)으로 None을 전달하면 현재까지 중첩된 total을 반환한다.
StopIteration 예외 발생시키기
- raise StopIteration(값)
코루틴도 제너레이트이므로 return을 사용하면 StopIteration이 발생한다.
그래서 return값은 raise StopIteration(값)처럼 사용할 수도 있다.
이렇게 직정 예외를 발생시키고 값을 지정하면 yield from으로 값을 가져 올 수 있다.
(파이썬 3.7부터는 제너레이터에서 raise로 StopIteration 예외를 직접 발생시키면 RuntimeError로 바뀌므로 사용할 수 없다.)
만들기
def accumulate():
total = 0
while True:
x = (yield)
if x is None:
raise StopIteration(total) #raise StopIteration(tatal)로 반환값을 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): # 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을 보내서 숫자 누적을 끝냄
이전 과 같은 코드지만 return total을 raise StopIteration(total)로 바꾸기만 한 것이고 결과 또한 똑같다.
yield from으로 값을 발생시키기
만들기
def number_coroutine():
x = None
while True:
x = (yield x) # 코루틴 바깥에서 값을 받아오면서 바깥으로 값을 전달
if x == 3:
return x
def print_coroutine():
while True:
x = yield from number_coroutine() # 하위 코루틴의 yield에 지정된 값을 다시 바깥으로 전달
print('print_coroutine:', x)
co = print_coroutine()
next(co)
x = co.send(1) # number_coroutine으로 1을 보냄
print(x) # 1: number_coroutine의 yield에서 바깥으로 전달한 값
x = co.send(2) # number_coroutine으로 2를 보냄
print(x) # 2: number_coroutine의 yield에서 바깥으로 전달한 값
co.send(3) # 3을 보내서 반환값을 출력하도록 만듦
결과
1
2
print_coroutine: 3
'Python > 기초문법' 카테고리의 다른 글
(35)데코레이터 사용하기 - 2 (0) | 2022.05.05 |
---|---|
(34)데코레이터 사용하기 (0) | 2022.05.04 |
(32)제너레이터 & yield (0) | 2022.04.25 |
(31)iterator & iterable 반복가능객체 (0) | 2022.04.24 |
(30)클래스 상속 사용하기 (0) | 2022.04.15 |