概述
0. 协程的优势
在Lua中有协程,在Python里也有协程(Coroutine
)。那为什么会需要协程,协程的好处有哪些?
1. 分片计算(sliced calculating
)
我们可以把一个原本很复杂的计算分成若干断,每次算一小段,然后每次获得一小部分的结果。这样就会降低卡顿现象的出现概率。
2. 手动暂停和恢复
这个好处就是,有一些东西我可以做到一半,当需要其他函数支持的时候,可以让那些函数先执行,结果返回后,再继续执行这些东西。
3. 可读性,可维护性,可扩展性
例如从经典的“消费者和生产者”模型的角度来看(生产者产生数据后,在其他地方消费它),使用协程编写的代码可读性更高。当然如果代码的可读性更高,那个他的可维护性和可扩展性也就更好。
那Python是如何支持协程(Coroutine
)的呢?
1. 使用协程和注意点
举个经典的栗子–素数筛(Sieve of primes
):给定一个较大值n
,寻找1~n
中的所有素数,并把它打印出来。
判断一个数m是否是素数,需要花费O(√n)
时间。当然是遍历并判断的话,需要花费O(n√n)
时间,太慢了,因此有人就提出了素数筛的方法,如下代码:
def prime_sieve(n):
flags = [True] * n
flags[0] = flags[1] = False
for i in xrange(2, n):
if flags[i]:
yield i
for j in xrange(2, (n - 1) / i + 1):
flags[i * j] = False
for p in prime_sieve(100):
print p
前面的prime_sieve
方法是生产者-把所有的素保存下来,后面的for-loop
是把消费者-保存下来的素数打印出来。
生产者实现也很简单,首先生成一个bool
列表表示该index
的数是否是素数;显然0,1
不是素数;接下来判断2<=i<=n
的数,如果为素数(True
),则yield
抛出,并把i的2倍,3倍,…,(n-1)/i+1
倍都设为不是素数(False
)。这就是素数筛的原理,花费时间为O(n)
。
而上面就是一个典型的Coroutine的使用:
在代码的消费者的
for-loop
中,调用prime_sieve
函数;在函数里,当使用关键字yield
时候,函数就会暂停,并且把素数i
抛出来。当loop
再次调用下一次prime_sieve
时,函数就会从yield
语句(抛出来的语句)下面一句恢复堆栈并且向下执行。
那上面的prime_sieve
和prime_sieve(100)
分别是什么东西呢?我们把他打印出来:
print prime_sieve(100) # >> <generator object at Ox00DB4EE0>
print prime_sieve # >> <function prime_sieve at 0x00DB6830>
因此判断一个对象是否是generator object
,就是看函数内部是否有yield
语句。
换句话说,如果一个函数中存在yield
语句,那么Python
就会认为这个函数调用就是一个generator object
。
那如何使用yield
语句,帮助我们实现第1节中的优势功能呢?请看下一个例子,实现了将list
或是tuple
中的每一个元素递归打印出来:
def item_iterator(embed_list):
for item in embed_list:
if isinstance(item, (tuple, list)):
item_iterator(item)
else:
yield item
lst = (0, (1, 2), (3, (4, 5)))
for item in item_iterator(lst):
print item
通过递归迭代打印,期望在代码中输出 "0 1 2 3 4 5"
,然而当你运行这段代码时,发现是输出一个"0"
。
问题出在item_iterator
的函数中存在yield
语句,所以他是一个generator object
,所以当在第4行递归使用item_iterator(item)
时,它并不是一个函数的递归调用,而是一个generator object
,所以正确item_iterator
的写法是这样的:
def item_iterator(embed_list):
for item in embed_list:
if isinstance(item, (tuple, list)):
for i in item_iterator(item)
yield i
else:
yield item
2. 总结
总之,当函数里面有个yield
,那个它就是一个generator object
,不要把这个generator
丢掉,如果要使用递归调用的话,切记再使用一个for-loop
,这样才会正确。
最后
以上就是超帅皮卡丘为你收集整理的Python中协程(Coroutine)的使用和注意点的全部内容,希望文章能够帮你解决Python中协程(Coroutine)的使用和注意点所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复