Python学习总结笔记(7)-- 生成器与协程
来源:互联网 发布:java输入输出流详解 编辑:程序博客网 时间:2024/05/24 06:19
在调用普通函数时,程序会中断调用代码运行,切换到调用函数的第一行代码开始执行,到return结束。并且将控制还给调用者,被调用函数状态结束并清空(局部变量等)。如果再次调用该函数,我们需要一切从头重新来过。生成器(协程)就是一种不同于这种模式的新方式,具有非常强大的功能,可以简化我们的代码逻辑,更可以降低内存消耗,提高代码质量与效率。
0x01 问题提出
来看下面一个例子。我们需要写一个函数,用来提取一个序列中的全部素数。输入参数是待提取的序列,返回提取的素数序列。
#/usr/bin/env python#coding:utf-8__author__ = 'kikay'import math#判断一个数值是否是素数def isPrime(iNum): if iNum>1: if iNum==2: return True if iNum%2==0: return False for i in range(3,int(math.sqrt(iNum)+1),2): if iNum%i==0: return False return True return False#列表生成方法def GetByList(iList): res=list() for i in iList: if isPrime(i): res.append(i) return resif __name__=='__main__': L=xrange(0,100) res=GetByList(L) print type(res) for i in res: print i,
输出结果:
<type 'list'>2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
0x02 生成器
上面的GetByList函数我们可以用生成器进行优化(优化代码以及内存空间),修改后完整代码如下:
#/usr/bin/env python#coding:utf-8__author__ = 'kikay'import math#判断一个数值是否是素数def isPrime(iNum): if iNum>1: if iNum==2: return True if iNum%2==0: return False for i in range(3,int(math.sqrt(iNum)+1),2): if iNum%i==0: return False return True return False#列表生成方法def GetByList(iList): res=list() for i in iList: if isPrime(i): res.append(i) return res#生成器def GetByGenerator(iList): return (i for i in iList if isPrime(i))if __name__=='__main__': L=range(0,100) #res=GetByList(L) res=GetByGenerator(L) print type(res) for i in res: print i,
运行结果:
<type 'generator'>2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
用GetByGenerator代替了函数GetByList,不仅代码更加简单,而且节约了内存。上面的改进体现在把返回的结果从List列表对象改成了Generator生成器对象,但是问题来了,我们需要提取小于某个数值(end)的全部素数,但是end的值不确定,可能非常大。修改后的代码如下:
#/usr/bin/env python#coding:utf-8__author__ = 'kikay'import math#判断一个数值是否是素数def isPrime(iNum): if iNum>1: if iNum==2: return True if iNum%2==0: return False for i in range(3,int(math.sqrt(iNum)+1),2): if iNum%i==0: return False return True return False#列表生成方法def GetByList(iList): res=list() for i in iList: if isPrime(i): res.append(i) return res#生成器def GetByGenerator(iList): return (i for i in iList if isPrime(i))#生成器(含条件)def GetByGenerator(iList,iEnd): return (i for i in iList if isPrime(i) and iEnd>i)if __name__=='__main__': end=100 L=xrange(0,end) #res=GetByList(L) res=GetByGenerator(L,end) print type(res) for i in res: print i,
运行结果:
<type 'generator'>2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
0x03 生成器函数
上面较好地解决了提出的问题,但是我们可以看到,函数输入参数中还是带了1个List对象(或者Generator对象),计算完毕后返回了1个Generator对象。那么有没有可能不需要输入Generator对象呢?答案是可以的,我们可以进一步定义Generator函数,改写后的代码如下:
#/usr/bin/env python#coding:utf-8__author__ = 'kikay'import math#判断一个数值是否是素数def isPrime(iNum): if iNum>1: if iNum==2: return True if iNum%2==0: return False for i in range(3,int(math.sqrt(iNum)+1),2): if iNum%i==0: return False return True return False#列表生成方法def GetByList(iList): res=list() for i in iList: if isPrime(i): res.append(i) return res#生成器def GetByGenerator(iList): return (i for i in iList if isPrime(i))#生成器(含条件)def GetByGenerator(iList,iEnd): return (i for i in iList if isPrime(i) and iEnd>i)#生成器函数def GetByGenerator_Fun(iEnd): i=0 while i<iEnd: if isPrime(i): yield i i+=1if __name__=='__main__': #模拟最大值 end=100 #L=xrange(0,end) #res=GetByList(L) #res=GetByGenerator(L,end) #print type(res) f=GetByGenerator_Fun(end) print type(f) for i in f: print i
运行结果:
<type 'generator'>2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
上面有一处yield。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行(把函数改成generator后,基本上不会用next()来调用它,而是直接使用for循环来迭代,见上面的例子)。
0x04 协程
前面我们演示了关于素数的例子。现在我们改下需求:找出比某个数的等比数列大的最小素数(比如num=10,我们要生成比“10,100,1000,10000,…”大的最小素数)。先看下用协程思路来完成的代码:
#/usr/bin/env python#coding:utf-8__author__ = 'kikay'import math#判断一个数值是否是素数def isPrime(iNum): if iNum>1: if iNum==2: return True if iNum%2==0: return False for i in range(3,int(math.sqrt(iNum)+1),2): if iNum%i==0: return False return True return False#生成器函数def GetByCoroutine(iNum): while True: if isPrime(iNum): iNum=yield iNum iNum+=1#获取最小的素数def GetMinValue(iIter,iBase): f=GetByCoroutine(iBase) #启动第一次 f.send(None) #或者f.next() #开始迭代 for i in xrange(1,iIter): #传递等比数列的下一个数字 iTemp=f.send(iBase**i) print iTemp,if __name__=='__main__': #基数 base=10 #等比数列迭代次数 iter=5 GetMinValue(iter,base)
上面的过程就是利用协程来完成的。有几点做下说明:
(1)iNum=yield iNum
yield关键字返回了当前iNum的值,而iNum=yield iNumde 意思是生成器函数将“冻结”,直到收到“激活”值,即收到从调用者传来的值(通过send、next等),并赋值给iNum。
(2)iTemp=f.send(iBase**i)
send首先取回被调用的生成器函数yield返回的值(用iTemp接收),同时给yield赋值(iBase**i),并激活生成器函数的下次迭代。
(3)f.send(None)
当用send来“启动”生成器函数的时候,必须首先发送None(或者调用next函数)。因为如果不发送,生成器函数无法去接收我们下面发送的值(iTemp=f.send(iBase**i))。
从上面的例子可以看出,协程比较类似于多线程,但是协程的特点是其在1个线程中执行,不需要来回切换线程,极大减小了消耗。同时,由于是在同一线程中执行,自然不需要使用同步锁(参考博客:Python学习总结笔记(3)–多线程与线程同步)。协程在同一个线程中执行,在多CPU环境下,可以利用多进程+协程,既充分利用多核,又充分发挥协程优点,可以获得极大效率。
0x05后记
生成器和协程是非常强大的工具。可以简化我们的代码逻辑,更可以降低内存消耗,提高代码质量与效率。
Python对协程的支持只是提供了基本的功能,并不完全。诸如gevent库等提供了完善的协程功能,在后续博客中会发布相关内容。
- Python学习总结笔记(7)-- 生成器与协程
- python 学习笔记(6)闭合与生成器
- Python学习笔记:生成器与迭代器
- Python学习笔记-生成器
- Python学习笔记--生成器
- python学习笔记 生成器
- Python生成器学习笔记
- python学习笔记--生成器
- python学习笔记(7)-高级特性(三)-列表生成式与生成器
- python生成器与协程
- Python学习笔记 - 生成器generator
- python学习笔记--理解生成器
- python 学习笔记6 生成器
- 【Python 学习笔记】各种生成器
- python学习笔记--generator生成器
- python学习笔记-生成器并行
- python学习笔记之生成器
- python-cookbook学习笔记八 迭代器与生成器一
- 队列实现总结:
- 11.24 作业 Problem F: 老师的点名方案
- map , multimap , set , multiset的用法
- Linux/Unix系统编程手册二:系统编程概念
- php 去掉二维数组中的空数组
- Python学习总结笔记(7)-- 生成器与协程
- 关于开源资料的寻找,避免重复造轮子:
- yii2 cookie
- ES:cluster.routing.allocation.disk.watermark.low
- Java Concurrency in Practice :基础知识(重排序与可见性)
- struts功能简要汇总
- mysu的实现
- ubuntu忘记忘记密码并恢复
- 用 Xcode 7 建立依赖其它第三方库静态库