一、什么是生成器?
在python中,使用了yield的函数被称为生成器,生成器返回的是一个迭代器的函数,只能用于迭代操作,在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。
生成器应用实例,利用生成器输出1—1亿的数。
list = (i for i in range(1,100000000))
for item in list:
print(item)
二、如何创建一个生成器?如何使用生成器?
(1)使用函数创建
1.函数内部需要实现一个循环体,并实现返回值推导算法,并由yield返回每次推导出来的值
2.yield关键词,其核心作用是
1.类似return,将指定值或多个值返回给调用方
2.记录此次返回或遍历的位置,返回数值之后,挂起,直到下一次执行next函数 再重新从挂起点接着运行(类似于断点)
def fib(max):
a, b = 1, 1
while a < max:
yield a # generators return an iterator that returns a stream of values.
a, b = b, a + b
for n in fib(15):
print(n)
运行结果:
(2)使用生成器推导式
核心点如下:
1.整体规律,类似类表生成推导式
2.只是语法,由之前的[ ],变成()
# 使用推导式,对于小于10的,乘3,对于大于等于10的,乘5
# 此时返回的不再是列表,而是一个生成器
g=(i*3 if i<10 else i*5 for i in range(100))
运行结果:
三、yield与return对比
1.相同点:
(1)均在函数体内使用,并且向调用方返回结果
(2)均可返回一个值或者多个值,如果是多个值,则是以元组格式返回
2.不同点:
1.包含yield的函数,调用时最终返回的是一个生成器,单纯的return函数,调用时返回的是一 个值。
2.return执行并返回值后,便会退出函数体,该函数体内存空间即回收释放
3.yield执行并返回值后,不会退出函数体,而是挂起,待下次next时,再从挂起点恢复运行
4.yield语句可以接受通过生成器send()方法传入的参数并赋值给一个变量,以动态调整生成器 的行为表现
5.yield语句的返回值,可以通过from关键词指定返回源
3.return在生成器中的作用:
1.在一个生成器函数中,如果没有return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出StopIteration终止迭代。
#1、yield和return共存
def gene(maxcount):
a,b=0,1
count=1
while True:
if count>maxcount:
#直接退出函数体
return
else:
#返回值后,函数在该处挂起,下次再从该处恢复运行
yield a+b
a,b=b,a+b
count+=1
#2、yield接受通过send传入的参数并动态调整生成器行为
#
def gene(maxcount):
a,b=0,1
count=1
while True:
if count>maxcount:
return
else:
msg=yield a+b
if msg=='stop':
return
a,b=b,a+b
count+=1
g=gene(10)
next(g)
g.send('msg') #生成器终止,并抛出一个StopIteration异常
#3、通过from关键词,接受另外一个生成器,并通过该生成器返回值
#此处只是做展示,大家知道即可,后续如果有类似场景,可以想起来可以这么搞就行
gene1=(i for i in range(10))
def gene2(gene):
yield from gene
g=gene2(gene1)
四、基本应用举例
(1)可控文件读取
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
(2)协程
进程、线程和协程的概念:
1.进程指单独的一个CPU运行程序,可以简单认为一个进程就是一个独立的程序
2.线程是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中的实际运作单位
3.协程可以认为是同一个线程内运行的代码
4.进程包含线程,线程包含协程
5.进程、线程的切换和调度,一般由操作系统自动完成,具体调度和切换机制较为复杂
6.同一线程下,多个协程的切换是由自己编写的代码进行控制,可以实现个性化的调度和切换需求
协程的特点:
1.协程是非抢占式特点:协程也存在着切换,这种切换是由我们用户来控制的。协程主解决的是IO的操作
2.协程有极高的执行效率:因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显
3.协程无需关心多线程锁机制,也无需关心数据共享问题:不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多
协程借助生成器实现的基本思路:
1.因为生成器通过yield,可以挂起,待下次执行时再次从挂起点恢复运行,满足切换和交替运行的特点
2.因为生成器可以通过send函数,动态的干预指定生成器的功能和表现,为实现多个协程之间协作提供了可能
#让两个函数交替运行
#核心就是把两个正常的函数使用yield变为生成器函数,然后交替使用其next调用即可
def task1(times):
for i in range(times):
print('task1 done the :{} time'.format(i+1))
yield
def task2(times):
for i in range(times):
print('task2 done the :{} time'.format(i+1))
yield
gene1=task1(5)
gene2=task2(5)
for i in range(100):
next(gene1)
next(gene2)