生成器
生成器的概念
在python中,一边循环一边计算的机制叫做生成器,我们设想一个场景:如果我要列举一万个数字,我可以先创建一万的数存入列表中或者挖掘这一万个数之间的规律然后通过一边计算一边循环来一个一个得到这一万个数。
你可以类比游戏中的地图一般是不会一下全部载入的,而是随着人物移动而分步加载的,加载新的区域,删除旧区域,这就类似生成器的机制。
生成器的特点
生成器采用延时计算(以时间换空间),我们使用算法来一步步推算元素,这样更加具有弹性,
它可以根据意愿来选择停止的节点,这样的好处就是不用耗费大量内存的情况下满足我们的需要。
生成器的语法
生成器表达式
在以往的学习中,我们学习过列表推导式:
lst = [x for x in range(10)]
这样我们可以得到0-9的列表,同样地,我们只需将[ ]改成( )就可以得到我们的生成器表达式
g = (x*x for x in range(10))
print(g)#<generator object <genexpr> at 0x000001A0CA096890>
生成器函数
生成器函数是一种阻塞函数,何为阻塞?我们如果学过并发知识,我们会了解异步协程时遇到IO流,这个阶段会花费一段时间,但后续过程我们又必须使用这个IO得到的对象,所以我们会停下来等待IO结束。
生成器函数通过yield关键字来实现阻塞操作,yield如return一般可以做到返回的效果,但是它并不会结束跳出函数体,而是阻塞在这一条语句的位置,静待下一次迭代的到来,当新的迭代到来,
代码从yield下一条语句开始,直到再没有yield语句后抛出StopIteration退出。
def gen():
i = 0
while i < 10:
res = yield i
print(f"res:{res}")
i += 1
return
if __name__ == '__main__':
g = gen()
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
我们发现res的值都是none,这是因为阻塞后的下次迭代是从yield下一条语句开始的,yield先返回了i的值,它的下一条语句是赋值语句,但这时已经没有值可以赋给res了,所以res每次都是空值。
send方法
def gen():
i = 0
while i < 10:
res = yield i
print(f"res:{res}")
i += 1
return
if __name__ == '__main__':
g = gen()
print(g)
print(next(g))
print(g.send(100))
print(g.send(200))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
对比之前的结果,res终于不是none了,这是因为我们使用了send方法 ,send的作用是唤醒并继续执行,发送一个信息到生成器内部,这时挂起后终于有值可以赋值给res,所以res不为none。
迭代器
迭代器概念
在介绍迭代器概念前,我们先回想一下什么时候我们会使用迭代器呢?一般在迭代循环时我们可以找见迭代器或者一些可迭代对象,这给我们一种印象:迭代器与循环密不可分。
概念:
1.迭代是Python最强大的功能之一,是访问集合元素的一种方式。
2. 迭代器是一个可以记住遍历的位置的对象。
3. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
4 .迭代器只能往前不会后退。 5 迭代器有两个基本的方法: iter() 和 next()
可迭代对象与迭代器
可迭代对象(Iterable object): 实现了__iter__方法的对象
迭代器(Iterator):实现了__iter__和__next__方法的对象,或者称为实现了__next__方法的可迭代对象。
from collections.abc import Iterator
from collections.abc import Iterable
def gen():
i = 0
while i < 10:
res = yield i
print(f"res:{res}")
i += 1
return
if __name__ == '__main__':
g = gen()#生成器函数
print(isinstance(g,Iterable))
print(isinstance(g,Iterator))
print(isinstance([],Iterable))
print(isinstance([],Iterator))
通过代码验证,我们得到:生成器是迭代器,而列表,集合等都是可迭代对象。
为什么 list 、 dict 、 str 等数据类型不是 Iterator ?
Python的 Iterator 对象表示的是一个数据流。可以把这个数据流看 做是一个有序序列,但我们却不能提前知道序列的长度,只能 不断通过 next() 函数实现按需计算下一个数据,所以 Iterator 的计算 是惰性的,只有在需要返回下一个数据时它才会计算。 所以,生成器一定是迭代器。 Iterator 甚至可以表示一个无限大的数据流,例如全体自然数。而 使用list是永远不可能存储全体自然数的。
for循环本质
for x in [1,2,3,4,5]:
pass
本质上是:
it = iter([1, 2, 3, 4, 5])
while True:
try:
x = next(it)
except StopIteration:
break