需要掌握:列表生成式
介绍
python中的生成器可用于for...in循环和next()函数
应用场景
- 封装循环算法。可将复杂的循环算法封装成生成器
- 节省内存空间。如:生成0,1,2…1000000的列表,占用内存空间大,此时可通过生成器实现
重要性
- 协程 是通过yield实现的
- 在深度学习中,也会用到生成器
实现方式一:类似列表生成式
生成0,1,2…1000000的列表,占用内存空间大,此时可通过生成器实现,通过下面例子模拟这一场景
回顾列表生成式:
l = [x * 3 for x in range(10)]
print(l)
执行结果:
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
生成器 是将 列表生成式 外面的[]换成()
gen = (x * 3 for x in range(5))
print(gen)
执行结果:
<generator object <genexpr> at 0x131f94650>
gen是生成器generator
判断是否生成器
from inspect import isgenerator
print(isgenerator(gen))
print(isgenerator(l))
执行结果:
True
False
使用-next()
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
执行结果:
0
3
6
9
12
执行第6次:print(next(gen)),报错:
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-160-b737a58f1e5a> in <module>
----> 1 print(next(l))
StopIteration:
使用-for…in
for item in gen:
print(item)
执行结果:
0
3
6
9
12
实现方式二:通过 函数 和 yield 实现
当循环算法复杂时,将其封装成函数,通过下面例子模拟这一场景
def test_generator(num):
index=0
while index<=num:
yield index*3
print("gen:",index*3)
index+=1
使用
gen=test_generator(5)
print(gen)
执行结果:
<generator object <genexpr> at 0x131f94650>
gen是生成器generator
判断是否生成器
from inspect import isgenerator
print(isgenerator(gen))
执行结果:
True
使用-next() (分析函数内部运行过程)
gen=test_generator(5)
print(next(gen))
执行结果:
0
继续执行:
print(next(gen))
执行结果:
gen: 0
3
继续执行:
print(next(gen))
执行结果:
gen: 3
6
由此可知:
- 第一次执行
print(next(gen))函数,先执行next(gen),进入test_generator()函数从头开始执行 - 当执行到
yield index*3时,返回后面的值,然后函数暂停执行 - 然后接第1步执行
print(),打印0 - 再次执行
print(next(gen))函数时,先执行next(gen),进入test_generator()函数接第2步暂停位置 继续执行,即:执行print("gen:",index*3),打印gen: 0,然后继续执行 - 当执行到
yield index*3时,返回后面的值,然后函数暂停执行 - 然后接第4步执行
print(),打印3 - 再次执行
print(next(gen))函数时,重复执行4、5、6步
使用-for…in
同上
使用send()
可以通过send()给生成器 传值
见下面例子:
def test_generator(num):
index=0
while index<=num:
# 这里赋值
n=yield index*3
print("gen:",index*3,",n=",n)
index+=1
gen=test_generator(5)
通过next() 或 gen.send(None)启动
print(gen.send(None))
执行结果:
0
此时test_generator()函数在yield index*3处暂停
注意:启动时必须传入None,否则报错如下:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-2-19a0a15df732> in <module>
----> 1 gen.send('a')
TypeError: can't send non-None value to a just-started generator
再次调用,通过send()函数传值,如下:
gen.send('a')
执行如下:
gen: 0 ,n= a
3
在test_generator()函数暂停处继续执行,将'a'传入,相当于n=3,然后继续执行
总结
- 调用
send()函数时必须传值,否则报错 - 第一次调用时,必须穿None,否则报错
比较
与list、tuple、dict、set、str比较
从使用上看,类似 list、tuple、dict、set、str,可用于for...in循环
如果 list 元素很多,会占用很大内存,但生成器不会
与迭代器比较
从使用上看,与 迭代器 很像,可用于for...in循环和next()函数
注意:迭代器是 类,实现__iter__()和__next__()方法,
详见:https://www.malaoshi.top/show_1EF2D9P4Udhf.html