关于生成器,主要有如下几个 关键点的内容python
1、什么是generator ,为何要有generator?数组
2、两种建立生成器方式数据结构
3、yield关键字函数
4、generator 两个调用方法 next() 和send()spa
1、什么是generator ,为何要有generator?操作系统
generator 是一种方式 ,是一种借助于循环,而后不断产生值的一种机制。(我是能理解机制这种描述,想了个例子来讲明这个例子感受又不对。)协程
那么为何须要generator呢?1、 当你须要获取大量值的时候,咱们能够将值存在列表里面,而后循环,读出没一个值。这没毛病。对象
可是你知道数组这种数据结构,他是将全部的值都紧挨着放在一块的,blog
当咱们将这不少值,都放在内存中,主存是须要申请很大一块空间,内存
进行存放的,而咱们的操做系统还要执行其余的任务,也要用到主存。这样将会致使操做RAM大量被占用,其余任何执行不了,或者干脆,其余任务执行完了,把地方让出来给你,你再去执行。以上的描述你们能感受出来效率是很是低的。
但是咱们有的时候就是须要啊,须要获取这种大量数据。(什么状况下,其实我写了几个项目如今也没有用到过generator,不过知道python的协程就是用生成器实现的。)而生成器他经过循环,计算出的数据,也就是说每循环一次就产生一次数据。这样是不须要向上面同样大量占用内存空间的,至于你说你须要计算啊(对于cpu来你这点代码量,简直就是连9牛一毛都称不上,这大兄弟但是每秒钟,能计算几十亿次)。
2、两种生成器的建立方式
1 、(x for x in range) ,就是将列表推到值外边的列表换成了元组。
二、函数中包含yield 关键字
def f1(n): yield n obj = f1(1) print(obj) 返回值是这个东西 <generator object f1 at 0x0000000001F1FFC0> 是一个生成器对象
三 、 yield 关键字
咱们知道一个函数包含一个yield关键字 ,返回的就是一个generator ,那么这个yield关字是怎么运做的,他都有那些特性
请看以下代码,这个就是一个生产这消费者模型
def consumer(): r = 'ok' while True: n1 = yield r while not n1: print('若是走了这里,说明,下一次调用以前先将上一次的NONE给赋值了') print('consumer consume %s'%(n1)) r = '我不想吃包子,我想吃pizza' def producer(c): data =c.send(None) print(data ,'到底一次yield有没有返回值') n=0 while n <5: n+=1 print('生产者生产了%s'%n) data_2=c.send(n) print('消费者其实想的是%s'%(data_2)) obj =consumer() producer(obj)
我建议,若是你看了个人博客,能够复制一下代码去执行一下。就能总结出来yield关键字有以下特性:
一、yiled 关键字相似于return ,就是函数执行到这里后,就不在向下执行,而后返还一个返回值。可是(一提可是就要仔细看啊)当 执行到yield 关键字时,整个函数一个运行状态是还保存在内存,当下次再调用这个生成器时,会从yield开始,在向下执行,
而不是从函数开头重新执行一遍。而后循环到了yield 在卡住,等待下次再调用
二、yield 能够被传值,不过必需要经过send()方法
4、generator 两个调用方法 next() 和send()
咱们一直在强调,generator 是借助于循环,不断产生新的值,因此就能理解 ,通常一个生成器都应该是一个循环中被调用,产生一个新的值。
因此
def f1(n): while True: n +=1 yield n obj = f1(1) for i in obj: print(i) 你可尝试一下,无限循环的感受。
固然通常状况下,不会是无限循环 ,确定是有一个终止条件的。
以下
def f1(n):
while n<5:
n +=1
yield n
obj = f1(1)
j =0
while j<5:
j+=1
try:
print(next(obj))
except StopIteration as e:
print('别调用了,最多能计算这么多')
当生成器已经计算结束后,你在去调用会报错的 ,若是是直接用for 调用是不会报错的。其实for 结构里面就相似上面的代码,只是当循环完generator后,抓去了对应的异常。结束循环。
最后则是send 方法 , 而send 方法,他有两个做用,第一个是能够调用这个generator计算,第二个就是给yield赋值,在将上面的代码赋值一下
def consumer(): r = 'ok' while True: n1 = yield r while not n1: print('若是走了这里,说明,下一次调用以前先将上一次的NONE给赋值了') print('consumer consume %s'%(n1)) r = '我不想吃包子,我想吃pizza' def producer(c): data =c.send(None) print(data ,'到底一次yield有没有返回值') n=0 while n <5: n+=1 print('生产者生产了%s'%n) data_2=c.send(n) print('消费者其实想的是%s'%(data_2)) obj =consumer() producer(obj)
关于send 方法有一个特性,就是在第一次启动生成器的时候,要传一个None,或者先用next调用一下。缘由是,当第一次走到yield的时候,yield 直接将会返回值返回,而后此次执行就停掉了,并无发生赋值的操做,因此你传进来一个值,是没有效果的因此python的源码里面就作了处理。而第二次在在用send唤醒generaot ,send方法在传一个值,在将这个值传给yield 关键字。而后yield在赋值,而后整个函数再向下运行。
还有个小点,就是在函数调用,生成一个generatro对象时,函数是什么执行的,只有在被调用,或者被for循环时,才开始执行。