David Beazley's Curious Course on Coroutines and Concurrency in Python is the best coroutine tutorial I've seen.

It makes an essential distinction between generators, from which you pull data, like this:

def squares():
  for i in range(10):
    yield i * i # send data

for j in squares():
  print j

... and coroutines, through which you push data, like this:

def line_printer():
  buf = ''
  try:
    while True:
      buf += (yield) # receive data
      parts = buf.split('\n')
      if len(parts) > 1:
        # We've received 1 or more new lines, print them
        for part in parts[:-1]: print part
        buf = parts[-1]
  except GeneratorExit:
    # Someone has called close() on this generator, print
    # the last of the buffer
    if buf: print buf

# push random chars, and sometimes newlines, into the coroutine
import random
coroutine = line_printer()
coroutine.next() # start coroutine
for i in range(1000):
  random_char = chr(random.randint(ord('a'), ord('z') + 1))
  if ord(random_char) > ord('z'):
    random_char = '\n'
  coroutine.send(random_char)

coroutine.close()

I plan to spend a lot of time with coroutines in the next few months, in particular seeing how they can simplify coding in asynchronous Python web frameworks.