일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- concurrency
- Class
- Protocol
- 109. Convert Sorted List to Binary Search Tree
- Regular Expression
- kaggle
- 43. Multiply Strings
- data science
- 315. Count of Smaller Numbers After Self
- 컴퓨터의 구조
- Generator
- Substring with Concatenation of All Words
- Python Implementation
- t1
- 30. Substring with Concatenation of All Words
- Python Code
- 715. Range Module
- LeetCode
- Decorator
- DWG
- Python
- 파이썬
- 시바견
- 운영체제
- attribute
- iterator
- 밴픽
- shiba
- 프로그래머스
- Convert Sorted List to Binary Search Tree
- Today
- Total
Scribbling
Python: Coroutines 본문
Basic behavior of Coroutines
def coroutine():
print('started')
x = yield
print('received: ', x)
c = coroutine()
next(c)
c.send(3)
1) "next(c)" or "c.send(None)" primes the coroutine -> coroutine now waits at 'yield' expression
2) c.send(3) sets x to 3, re-executing the coroutine
3) At the end, coroutine raises StopIteration
Example: coroutine to compute a running average
def averager():
total, count = .0, 0
average = None
while True:
num = yield average
total += num
count += 1
average = total / count
a = averager()
next(a)
print(a.send(3))
print(a.send(5))
print(a.send(11))
A decorator can be used to prime a coroutine conveniently.
from functools import wraps
def coroutine(func):
@wraps(func)
def primer(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return primer
@coroutine
def averager():
total, count = .0, 0
average = None
while True:
num = yield average
total += num
count += 1
average = total / count
Generator.throw(): delivers an error to the generator. If the generator handles the exception, control flows until the next yield and generator.throw() receives the yielded value. If not handled, the exception is again delivered to the caller.
Generator.close(): raises GeneratorExit in the generator. If the generator does not handle the exception or raises StopIteration, none of the exceptions are delivered to the caller.
class SimpleException(Exception):
''' Test Exception '''
def coroutine():
print('started')
while True:
try:
x = yield
except SimpleException:
print('SimpleException handled. Continuing... ')
else:
print('received: ', x)
c = coroutine()
next(c)
c.send(11)
c.throw(SimpleException)
c.send(12)
c.throw(ZeroDivisionError)
c.send(13)
c.close()
If something should be executed with an unexpected exception, try/finally block can handle the case.
def coroutine():
print('started')
try:
while True:
try:
x = yield
except SimpleException:
print('SimpleException handled. Continuing... ')
else:
print('received: ', x)
finally:
print('Unexpected Exception; Ending Coroutine... ')
Couroutines can return values.
from collections import namedtuple
Result = namedtuple('result', 'count average')
def averager():
total, count = .0, 0
average = None
while True:
num = yield average
if num is None:
break
total += num
count += 1
average = total / count
return Result(count, average)
a = averager()
next(a)
print(a.send(3))
print(a.send(5))
print(a.send(11))
print(a.send(None))
a = averager()
next(a)
print(a.send(3))
print(a.send(5))
print(a.send(11))
try:
a.send(None)
except StopIteration as exc:
result = exc.value
print(result)
Returned value can be captured like above.
"yield from" is very useful in this case as it receives the returned value.
Using yield from
When generator is stopped at 'yield from', sub generator directly communicates with caller through send() and yield() methods. At the time when sub generator is completed, it raises StopIteration with returned value and generator comes to play again.
from collections import namedtuple
Result = namedtuple('result', 'count average')
def averager():
total, count = .0, 0
average = None
while True:
num = yield average
if num is None:
break
total += num
count += 1
average = total / count
return Result(count, average)
def grouper(results, key):
while True:
results[key] = yield from averager()
def caller(data):
results = {}
for key, values in data.items():
group = grouper(results, key)
next(group)
for value in values:
group.send(value)
group.send(None)
print(results)
data = {
'girls;kg': [41, 45, 47, 49, 51, 50.5, 49.5],
'girls;m': [157, 159, 161, 167, 167.5, 163.3]
}
caller(data)
* keep in mind that averager() is called for four times in the above code: When StopIteration is raised from sub-generator, generator code runs again. Within while loop, grouper again stops at yield from calling averager at the same time.
'Computer Science > Python' 카테고리의 다른 글
Zip & Unpacking (0) | 2022.05.10 |
---|---|
Python: Concurrency with Futures (0) | 2022.05.09 |
Python: Context Manager (0) | 2022.04.29 |
Python: Iterator, Generator (0) | 2022.04.22 |
Python: Operator Overloading (0) | 2022.04.20 |