In [10]:
def averager():
    total = 0.0
    count = 0
    average = None
    
    while True:
        d = yield average
        total +=d
        count += 1
        average = total / count

In [11]:
cor_avg = averager()

In [12]:
next(cor_avg)

In [14]:
cor_avg.send(12)


Out[14]:
11.5

use Decorators


In [17]:
from inspect import getgeneratorstate

In [21]:
from functools import wraps

def coroutine(func):
    @wraps(func)
    def primer(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen
    
    return primer

In [16]:
coro_avg = averager()

In [18]:
getgeneratorstate(coro_avg)


Out[18]:
'GEN_CREATED'

In [23]:
@coroutine
def averager():
    total = 0.0
    count = 0
    average = None
    
    while True:
        d = yield average
        total +=d
        count += 1
        average = total / count

In [24]:
coro_avg = averager()

In [25]:
getgeneratorstate(coro_avg)


Out[25]:
'GEN_SUSPENDED'

In [26]:
coro_avg.send(40)


Out[26]:
40.0

In [27]:
coro_avg.send(10)


Out[27]:
25.0

In [28]:
coro_avg.send('spam')


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-ab30dc25c38f> in <module>()
----> 1 coro_avg.send('spam')

<ipython-input-23-ed0a49874358> in averager()
      7     while True:
      8         d = yield average
----> 9         total +=d
     10         count += 1
     11         average = total / count

TypeError: unsupported operand type(s) for +=: 'float' and 'str'

In [29]:
coro_avg.send(15)


---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-29-7c83c48f0082> in <module>()
----> 1 coro_avg.send(15)

StopIteration: 

In [33]:
class DemoException(Exception):
    """"""
def demo_exc_handling():
    print("-> coroutine started")
    while True:
        try:
            x = yield
        except DemoException: #<1>
            print('*** DemoException handled.')
        else:
            print("-> coroutine received: {!r}".format(x))
    raise RuntimeError('This line should never run.')

In [34]:
exc_coro = demo_exc_handling()

In [35]:
next(exc_coro)


-> coroutine started

In [37]:
exc_coro.send(11)


-> coroutine received: 11

throw exceptions


In [38]:
exc_coro.send(11)


-> coroutine received: 11

In [40]:
exc_coro.throw(DemoException)


*** DemoException handled.

In [41]:
getgeneratorstate(exc_coro)


Out[41]:
'GEN_SUSPENDED'

In [ ]:


In [34]:
exc_coro = demo_exc_handling()

In [35]:
next(exc_coro)


-> coroutine started

In [37]:
exc_coro.send(11)


-> coroutine received: 11

In [42]:
exc_coro.throw(ZeroDivisionError)


---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-42-d0a75d13dc38> in <module>()
----> 1 exc_coro.throw(ZeroDivisionError)

<ipython-input-33-0a06ddc6e361> in demo_exc_handling()
      5     while True:
      6         try:
----> 7             x = yield
      8         except DemoException: #<1>
      9             print('*** DemoException handled.')

ZeroDivisionError: 

In [43]:
getgeneratorstate(exc_coro)


Out[43]:
'GEN_CLOSED'

In [ ]:

Handling exception


In [46]:
class DemoException(Exception):
    """"""
def demo_finally():
    print("-> coroutine started")
    try:
        while True:
            try:
                x = yield
            except DemoException:
                print("*** DemoException handled. Continuing...")
            else:
                print("->coroutine received: {!r}".format(x))
    finally:
        print("-> coroutine ending")

In [57]:
fin_coro = demo_finally()

In [58]:
next(fin_coro)


-> coroutine started

In [59]:
fin_coro.send(11)


->coroutine received: 11

In [60]:
fin_coro.send(12)


->coroutine received: 12

In [61]:
fin_coro.send("spam")


->coroutine received: 'spam'

In [56]:
fin_coro.throw(ZeroDivisionError)


-> coroutine ending
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-56-d0ec4ddb0825> in <module>()
----> 1 fin_coro.throw(ZeroDivisionError)

<ipython-input-46-8c33fb2f1f1e> in demo_finally()
      6         while True:
      7             try:
----> 8                 x = yield
      9             except DemoException:
     10                 print("*** DemoException handled. Continuing...")

ZeroDivisionError: 

In [ ]:

Return a value from Coroutine


In [62]:
from collections import namedtuple

In [64]:
Result = namedtuple('Result', 'count average')

In [65]:
def averager():
    total = 0.0
    count = 0
    average = None
    
    while True:
        term = yield
        if term is None:
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)

In [66]:
coro_avg = averager()

In [67]:
next(coro_avg)

In [68]:
coro_avg.send(10)

In [69]:
coro_avg.send(20)

In [70]:
coro_avg.send(None)


---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-70-a9c80bbec98f> in <module>()
----> 1 coro_avg.send(None)

StopIteration: Result(count=2, average=15.0)

In [ ]: