python 生成器 generator(yield)

需要掌握:列表生成式

介绍

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

由此可知:

  1. 第一次执行print(next(gen))函数,先执行next(gen),进入test_generator()函数从头开始执行
  2. 当执行到yield index*3时,返回后面的值,然后函数暂停执行
  3. 然后接第1步执行print(),打印0
  4. 再次执行print(next(gen))函数时,先执行next(gen),进入test_generator()函数接第2步暂停位置 继续执行,即:执行print("gen:",index*3),打印gen: 0,然后继续执行
  5. 当执行到yield index*3时,返回后面的值,然后函数暂停执行
  6. 然后接第4步执行print(),打印3
  7. 再次执行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


原文出处:/show_1EF5cbkNmHaf.html