+关注继续查看

# 协程

>>> 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):
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
>>> next(c)
>>> 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):
...     while True:
...         text = (yield)
...         if substring in text:
...             print('Oh no: I found a %s again!'
...                   % (substring))
...
>>> next(c)
>>> 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
...     while True:
...         text = (yield)
...         if substring in text:
...             print('Oh no: I found a %s again!'
...                   % (substring))
...
>>> 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  现在使用Python的协程来做（grep.py）： 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() parser.add_argument('-i', action='store_true', dest='case_insensitive') parser.add_argument('pattern', type=str) parser.add_argument('infile', type=argparse.FileType('r')) args = parser.parse_args() cat(args.infile, args.case_insensitive, grep(args.pattern, args.case_insensitive, count(args.pattern)))  分析代码之前，我们先运行一下，和grep进行比较： $ 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协程）

child.send(line)）。如果匹配是大小写不敏感的，不需要进行转换；如果大小写敏感，则都转化为小写。

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

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

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版本之后引入了asyncoreasynchat模块，开始支持异步编程。2.5版本引入了yield，可以向协程传递数据，简化了代码、加强了性能。Python 3.4 引入了一个新的库进行异步I/O，称作asyncio

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

|
2月前
|

21 0
|
3月前
|
NoSQL 中间件 关系型数据库
Python下篇 3. 异步编程生态
Python下篇 3. 异步编程生态
36 0
|
3月前
|

21 0
|
3月前
|

Python下篇 1. GIL对异步编程的影响
Python下篇 1. GIL对异步编程的影响
46 0
|
3月前
|

Python上篇：2. 异步编程的核心：epoll+callback+event loop三驾马车搞定异步编程
Python上篇：2. 异步编程的核心：epoll+callback+event loop三驾马车搞定异步编程
51 0
|
3月前
|

Python上篇：1. 为什么需要异步编程？
Python上篇：1. 为什么需要异步编程？
29 0
|
5月前
|

79 0
|
8月前
|
Java Python
Python异步: 什么是异步编程？ （1）

105 0
|
Linux Python Windows
python异步编程--回调模型(selectors模块)

1474 0
|

《Python分布式计算》 第7章 测试和调试分布式应用 （Distributed Computing with Python）

1265 0