我是靠谱客的博主 超帅皮卡丘,最近开发中收集的这篇文章主要介绍Python中协程(Coroutine)的使用和注意点,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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_sieveprime_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)的使用和注意点所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(28)

评论列表共有 0 条评论

立即
投稿
返回
顶部