首先说明一下,第一次写博客,难免会有纰漏,如果有不足的地方希望大家指正,谢谢
我的邮箱是 [email protected]
生成器都有什么用处
生成器可以用来存放一些数据,像列表元组那样,在生成器的高端用法里被用作协程(py3.4以前),可以用来实现一些异步操作。
哪些人会用到生成器,在什么场景下来用呢
在你有很多很多数据要存放在一个容器中时,但你并不着急需要用这些数据(也就是说这些数据可能会占用你的大部分内存),那么你很可能需要用到生成器。
或者当你在编写异步IO的项目时,你几乎逃不掉的要学习生成器,还有很多场景下都会用到生成器
那么如何来创建一个简单的生成器呢
很简单,就像一个列表推导式那样
lst = [i for i in range(5)] # 这样就快速创建了一个列表
生成器类似
gen = (i for i in range(5)) # 同样我们创建了一个生成器,这个生成器是一个可迭代对象
print(type(gen)) #
生成器的特性:
生成器可以认为是一个存放数据的集合,而且他是一个一次性的容器
当然这很简单,但是不要简单的认为,这就像快速生成了一个元组,千万不要这么认为,
因为这根本就不是一个元组。
我们将所要保存的数据0,1,2,3,4保存在了这个生成器中
那么当我们需要用到这些数据的时候,我们怎么将它们取出呢?我们不能在像以前的方式
通过下标来取出里边的值,那样是毫无结果的。
上文说过,生成器是一个可迭代对象,那么我们就可以用for..in结构,来一个一个的取出里边的元素,如:
for one in gen: # 遍历这个生成器,并将返回值打印出来
print(one)
0
1
2
3
4
当然这种做法也是很安全的,除此之外还有一种方法next(),
newgen = (i for i in range(2))
print(next(newgen)) # 0 注意需要重新在创建一个新的生成器,
# 接下来会作说明
print(next(newgen)) # 1
每调用一次这个方法将会取出这个集合里边的一个元素
但是,当取到最后一个元素后,如果对这个生成器使用next()方法将会引发StopIteration异常
print(next(newgen)) # StopIteration
这是为什么呢,因为生成器是一次性的,不回头的,这里是相对于像列表,元组,字符串等那样的集合类型而言
列表等集合是可以多次进行迭代(就是可以多次的遍历集合中的元素)的
而生成器不行,你可以理解生成器为 一个装有巧克力豆的盒子,每次用next()方法时都会从这个盒子中拿走,
真正的拿走一颗巧克力豆,那么这个盒子里的巧克力豆就会真正的减少一颗,当把最后一颗巧克力豆拿走时,
如果你再来拿,那么就会抛出异常,因为盒子都没有巧克力豆了,还怎么拿。
而像列表那样的集合也可以认为是装有巧克力豆的盒子,但是这个盒子里的东西只能看,不能带走,每次看完之后还得按照原来的顺序把它放回去。
那么你应该对生成器有一定的了解了吧。
另外,我们还可以玩一点更厉害的,希望你学了函数,那么继续介绍一下
def gen(n):
# 在这里你可以做更多的别的厉害的操作,
# 而不是只是像用生成器表达式那样
# 只是做有限的操作,示例如下
for i in range(n):
yield i # 注意在函数中如果出现了yield(这是一个关键字),
# 那么这个函数就是一个生成器函数,
# 可以对其进行一些向生成器那样的操作,如next()等。
g = gen(3) # 同生成器一样,需要创建一个生成器对象,
#然后就能对其进行操作了
for one in g:
print(one, end=' ')
# 0 1 2
小结一下
生成器函数比生成器表达式能玩出更多的花样,在yield 之前可以做很多事,另外在生成器函数中并没有限定只能有一个yield,可以多次yield,也可以出现return,但是在函数中遇到return ,那么函数就是调用结束了,我在类比一下,::
在生成器函数中
可以把函数认为是一场 需要做的任务,这个任务的量很大,但是在完成整个任务前必须要提交一些已经完成的事情,在任务的最后必须要提交任务结果(这场任务的最终结果)
------------------------------------------------------------------类比下:
import time
def demo():
work = 10
now = 0
time.sleep(1) # 模拟工作时长
now += 3
yield now # 目前还没有完成最终的任务,先提交一部分
time.sleep(1) # 同上
now += 6
yield now # 还没做完,再提交一部分
time.sleep(0.2)
return now # 终于做完了,提交最终的结果
yield 123 # 这些都不会执行,因为return 就是函数的终点
print('hello world') # 也不会执行
注意,当函数运行到return时,这个函数就算结束了,即使下面还有代码有不会执行到,如上边注释的例子。
除此之外,对一个生成器对象还有其他的一些函数可以使用,如send(),throw()等
接下来我来逐一介绍
send()顾名思义,就是向生成器里送东西,注意,在执行这个函数前需要明确知道,在生成器内部运行到哪了,这个哪一共有三种状态,以一种是目前生成器还没有启动,那么这个时候就不能向生成器里送除了None以外的任何值,否则将会引发异常,PS:我认为next()方法就相当于send(None)方法,send()方法也是拨动一下这个生成器,直到这个函数运行到下一个yield或者return为止。
第二种是这个生成器已经运行到某一个yield了,那么现在就可以往这个生成器里送东西了,实例代码如下:
def gen(n):
for i in range(n):
print(i)
print('*'*20)
recv = yield i
print(recv)
print('#'*20)
g = gen(3)
result = g.send(None) # next(g)
g.send(22)
print(result)
# 运行结果如下:
0
********************
22
####################
1
********************
0
由此可以发现,当send(None)后,生成器运行到第一个yield,并且这个函数暂时交出cpu的控制权(完成一部分想歇会,暂时不干了),函数内部的print(i) 最先执行,然后打印出了*号,这时运行到yield,因为yield 也可以将某些数据带出去,这个时候result就接收了刚才yield出来的值,也就是最后打印出来的0,因为第一次的i就是0嘛。这个时候已经从函数中出来了,要运行g.send(22),这样就把22这个数据带进了生成器的内部,&&PS:我认为这个一点很强,因为这就相当于一个内部的函数啊,那就可以送进去几乎是任何类型的数据了,无论字典元组字符串列表,甚至是函数,类都可以,但是我很少,基本没见过有人这么用过,可能没必要这么做。示例代码如下:等会再上代码。PS结束&&
送进去的22会被recv接收(可以认为那个就是赋值语句,从右往左执行)在print(recv) 就会打印22,然后再打印#号,由于这个循环还没有运行完,将再循环一次,再打印1,这时候i变成1了嘛,再打印*号,遇到yield就返回,这就是一个比较复杂的生成器函数。
第三种情况就是这个生成器出现了异常,那么将无法对这个生成器使用任何的方法
PS: 上边说的示例代码:
def gen(n):
for i in range(n):
print(i)
print('*'*20)
recv = yield i
print(recv)
recv[1]('hello world')
print('#'*20)
g = gen(3)
g.send(None) # next(g)
g.send((1,print))
# result ---------------------------------------
0
********************
(1, )
hello world
####################
1
********************
另一个方法throw()顾名思义,抛出点什么东西,那就是抛出异常:
就是将这个生成器给玩坏,这个方法很简单,具体的实际用处在将来要介绍的协程中会用到,在生成器内部抛出异常,终止生成器的操作。
简单使用直接上代码
def gen(n):
for i in range(n):
print(i)
yield i
g = gen(5)
g.send(None) # next(g)
g.send(1)
try: # 捕获异常,这里一定会产生异常
g.throw(Exception('参数是一个异常类'))
except:
pass
# 注意,如果不是手动用throw()把生成器玩坏的话,那么此时这个生成器应该还没# 走到头(巧克力豆还没全部拿完)。但是现在已经把这个盒子给弄坏了,所以如# 果在来盒子里拿东西,将会触发异常,盒子都坏了,你还拿什么东西?
g.send(1)
抛出 StopIteration 异常。
我所知道的生成器的简单应用就这些,如果大家还有什么补充的话可以通过邮箱或者来通知我,希望大家能一起进步。
如果感觉读我的文章能读懂的话,可以点个赞支持一下哦,谢谢
感觉我的例子不是很贴切模型的话也可以在下方评论,希望大家能指出我的不足之处,如果对我有什么建议及意见也要指出来哦,比如再写点别的知识之类的。
总之谢谢各位的耐心观看 _