# 协程

```>>> for i in range(3):
...     print(i)
...
1
2
>>> for line in open('exchange_rates_v1.py'):
...     print(line, end='')
...
#!/usr/bin/env python3
import itertools
import time
import urllib.request
…```

```class MyIterator(object):
def __init__(self, xs):
self.xs = xs

def __iter__(self):
return self

def __next__(self):
if self.xs:
return self.xs.pop(0)
else:
raise StopIteration

for i in MyIterator([0, 1, 2]):
print(i)```

```1
2
3```

```itrtr = MyIterator([3, 4, 5, 6])

print(next(itrtr))
print(next(itrtr))
print(next(itrtr))
print(next(itrtr))

print(next(itrtr))```

```3
4
5
6
Traceback (most recent call last):
File "iteration.py", line 32, in <module>
print(next(itrtr))
File "iteration.py", line 19, in __next__
raise StopIteration
StopIteration```

```def mygenerator(n):
while n:
n -= 1
yield n

if __name__ == '__main__':
for i in mygenerator(3):
print(i)```

```2
1
0```

```>>> from generators import mygenerator
>>> mygenerator(5)
<generator object mygenerator at 0x101267b48>```

```>>> g = mygenerator(2)
>>> next(g)
1
>>> next(g)
0
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration```

• `yield()`： 用来暂停协程的执行
• `send()`： 用来向协程传递数据（以让协程继续执行）
• `close()`：用来关闭协程

```def complain_about(substring):
print('Please talk to me!')
try:
while True:
text = (yield)
if substring in text:
print('Oh no: I found a %s again!'
% (substring))
except GeneratorExit:
print('Ok, ok: I am quitting.')```

```>>> from coroutines import complain_about
>>> c = complain_about('Ruby')
>>> next(c)
Please talk to me!
>>> c.send('Test data')
>>> c.send('Some more random text')
>>> c.send('Test data with Ruby somewhere in it')
Oh no: I found a Ruby again!
>>> c.send('Stop complaining about Ruby or else!')
Oh no: I found a Ruby again!
>>> c.close()
Ok, ok: I am quitting.```

```>>> c.send('Test data')
>>> c.send('Some more random text')
>>> c.send('Test data with Ruby somewhere in it')
Oh no: I found a Ruby again!```

```>>> c.close()
Ok, ok: I am quitting.```

```>>> def complain_about2(substring):
...     print('Please talk to me!')
...     while True:
...         text = (yield)
...         if substring in text:
...             print('Oh no: I found a %s again!'
...                   % (substring))
...
>>> c = complain_about2('Ruby')
>>> next(c)
Please talk to me!
>>> c.close()
>>> c.send('This will crash')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> next(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration```

```>>> def coroutine(fn):
...     def wrapper(*args, **kwargs):
...         c = fn(*args, **kwargs)
...         next(c)
...         return c
...     return wrapper
...
>>> @coroutine
...     print('Please talk to me!')
...     while True:
...         text = (yield)
...         if substring in text:
...             print('Oh no: I found a %s again!'
...                   % (substring))
...
>>> c = complain_about2('JavaScript')
Please talk to me!
>>> c.send('Test data with JavaScript somewhere in it')
Oh no: I found a JavaScript again!
>>> c.close()```

# 一个异步实例

```\$ curl -sO http://www.gutenberg.org/cache/epub/2600/pg2600.txt
\$ wc pg2600.txt
65007  566320 3291648 pg2600.txt```

```\$ time (grep -io love pg2600.txt | wc -l)
677
(grep -io love pg2600.txt) 0.11s user 0.00s system 98% cpu 0.116 total```

```def coroutine(fn):
def wrapper(*args, **kwargs):
c = fn(*args, **kwargs)
next(c)
return c
return wrapper

def cat(f, case_insensitive, child):
if case_insensitive:
line_processor = lambda l: l.lower()
else:
line_processor = lambda l: l

for line in f:
child.send(line_processor(line))

@coroutine
def grep(substring, case_insensitive, child):
if case_insensitive:
substring = substring.lower()
while True:
text = (yield)
child.send(text.count(substring))

@coroutine
def count(substring):
n = 0
try:
while True:
n += (yield)
except GeneratorExit:
print(substring, n)

if __name__ == '__main__':
import argparse

parser = argparse.ArgumentParser()
dest='case_insensitive')

args = parser.parse_args()

cat(args.infile, args.case_insensitive,
grep(args.pattern, args.case_insensitive,
count(args.pattern)))```

```\$ time python3.5 grep.py -i love pg2600.txt
love 677
python3.5 grep.py -i love pg2600.txt  0.09s user 0.01s system 97% cpu 0.097 total```

• 逐行读取文件（通过`cat`函数）
• 统计每行中`substring`的出现次数（`grep`协程）
• 求和并打印数据（`count`协程）

`grep`命令是我们的第一个协程。这里，进入一个无限循环，持续获取数据（`text = (yield)`），统计`substring``text`中的出现次数，，将次数发送给写一个协程（即`count`）：`child.send(text.count(substring)))`

`count`协程用总次数`n`，从`grep`获取数据，对总次数进行求和，`n += (yield)`。它捕获发送给各个协程关闭时的`GeneratorExit`异常（在我们的例子中，到达文件最后就会出现异常），以判断何时打印这个`substring``n`

```def coroutine(fn):
def wrapper(*args, **kwargs):
c = fn(*args, **kwargs)
next(c)
return c
return wrapper

def cat(f, case_insensitive, child):
if case_insensitive:
line_processor = lambda l: l.lower()
else:
line_processor = lambda l: l

for line in f:
child.send(line_processor(line))

@coroutine
def grep(substring, case_insensitive, child):
if case_insensitive:
substring = substring.lower()
while True:
text = (yield)
child.send(text.count(substring))

@coroutine
def count(substring):
n = 0
try:
while True:
n += (yield)
except GeneratorExit:
print(substring, n)

@coroutine
def fanout(children):
while True:
data = (yield)
for child in children:
child.send(data)

if __name__ == '__main__':
import argparse

parser = argparse.ArgumentParser()

args = parser.parse_args()

cat(args.infile, args.case_insensitive,
fanout([grep(p, args.case_insensitive,
count(p)) for p in args.patterns]))```

```\$ time python3.5 mgrep.py -i love hate hope pg2600.txt
hate 103
love 677
hope 158
python3.5 mgrep.py -i love hate hope pg2600.txt  0.16s user 0.01s system 98% cpu 0.166 total```

# 总结

Python从1.5.2版本之后引入了`asyncore``asynchat`模块，开始支持异步编程。2.5版本引入了`yield`，可以向协程传递数据，简化了代码、加强了性能。Python 3.4 引入了一个新的库进行异步I/O，称作`asyncio`

Python 3.5通过`async def``await`，引入了真正的协程类型。感兴趣的读者可以继续研究Python的新扩展。一句警告：异步编程是一个强大的工具，可以极大地提高I/O密集型代码的性能。但是异步编程也是存在问题的，而且还相当复杂。

0 条评论

• ### 深入理解Python异步编程（上）

彻底理解异步编程是什么、为什么、怎么样。深入学习asyncio的基本原理和原型，了解生成器、协程在Python异步编程中是如何发展的。

• ### 《Python Cookbook》第三版最佳译本

目前看到的最好的《Python Cookbook》译本，网盘下载地址： https://pan.baidu.com/s/1pL1cI9d

• ### 《Scikit-Learn与TensorFlow机器学习实用指南》 第5章 支持向量机

支持向量机（SVM）是个非常强大并且有多种功能的机器学习模型，能够做线性或者非线性的分类，回归，甚至异常值检测。机器学习领域中最为流行的模型之一，是任何学习机器...

• ### 认知谜题：E. 人类找不到“外星智慧生物”的5种假说

对于著名的“费米悖论”，为什么人类一直找不到“外星智慧生物”？这5种假说可以直观的解释。

• ### Spring BeanPostProcessor 接口

上篇文章提到的 Spring Aware 接口，是针对某个实现这些接口的 Bean 定制初始化的过程，不过 Spring 同样可以针对容器中的所有 Bean，或...

• ### 华为、联想 谁能帮助黑莓重返中国？

在写这篇文章之前，我还要解释一下关于愚人节《华为收购黑莓中国尘埃落定? 让联想很忧伤》一文，当时没有想到这篇文章会让黑莓的股票出现小幅上涨，更没有想到这篇文章引...

• ### 【编程经验】关于链表、还有编译器

关注我们 最近有小白来问VC6.0和其他编译器怎么下，小编回了一些，但是也是确实比较多......所以今天就不单单分享知识了，还要分享资源！ ...