python之生成器

一、生成器介绍

什么是生成器( generator)

  • 生成器就是一个自定义迭代器

  • 函数体内含有yield关键字

为何要用生成器?

  • 为了节省内存

创建生成器的两种方式

  • 调用yield关键字

  • 使用生成器表达式

如何用生成器

函数体内但凡出现yield关键字,调用函数将不会触发函数体代码的运行,而是会返回一个生成器对象,生成器本质就是一个迭代器

二、生成器与yield

如何得到自定义的迭代器

在函数内定义yield关键字, 调用函数并不会执行完函数体代码, 会返回一个生成器对象(生成器就等同于迭代器)

注意:

一旦函数体内出现yield关键字, 再去调用函数, 就和函数体代码没有关系了, 得到的就是一个生成器, 你的函数体代码的生效, 取决与你的next

示例
def func():
    yield 1
    yield 2
    yield 3res_generator = func()
# 返回一个生成器对
print(res_generator)  # <generator object func at 0x00000246ECEF42E0># 生成器就等同于迭代器, 也有迭代器下的内置方法__iter__(),__next__(), 也有iter()和next()功能
res_generator.__iter__()
iter(res_generator)
# res_generator.__next__()
# next(res_generator)# 对定义yield关键字的函数,返回的生成器对象迭代取值
res1 = res_generator.__next__()
print(res1)  # 1res2 = res_generator.__next__()
print(res2)  # 2res3 = res_generator.__next__()
print(res3)  # 3# 补充: 之前我们使用的len()功能, 内部其实最后调用的还是内置的__len__()方法
res = len('aaa')  
print(res)  # 3res = 'aaa'.__len__()
print(res)  # 3# 需求(实例): 实现range功能
def my_range(start, stop, step=1):
    print("start...")
    while start < stop:
        yield start
        start += step
    print("stop...")
​
res = my_range(0, 10)
print(res)
​
for item in res:
    print(item)  # <generator object my_range at 0x00000142F62142E0>
    
'''
start...
0
1
2
3
4
5
6
7
8
9
stop...
'''

yield 与 return 的区别

  1. 相同点

    • 在返回值的及的角度,用法都一样

  2. 不同点

    • yield 可以返回多次值, 而 return 只能返回一次值

总结yield

  • 为我们提供了一种自定义的迭代器的方式

  • yield 可以暂停函数, 保存函数执行的状态, 然后使用 next 方法再次触发函数体代码的运行 (协程知识点)

示例应用:
  • 造一个无穷值

    def my_range():
        n = 0
        while True:
            yield n
            n += 1
    obj = my_range()    # 一个生成器
    print(obj)          # <generator object my_range at 0x0000022784809F48>输出的是老母鸡内存地址# 使用"next"取值
    print(next(obj))    #  0
    print(next(obj))    #  1
    print(next(obj))    #  2
    print(next(obj))    #  3  
    ......等等..
    ​
    # 使用 "for" 循环
    for i in my_range():  # 也可以使用 for 循环取
        print(i)
    
  • 模仿内置函数range()

    def my_range(start, stop, step = 1):
        n = start
        while n < stop:
            yield n
            n += step
    # 使用 "next" 取值
    obj = my_range(2 ,14, 2)
    print(next(obj))     # 2
    print(next(obj))     # 4
    print(next(obj))     # 6# 使用 "for" 循环
    for line in my_range(2, 15, 3):
        print(line)
    

yield 的应用

  • next : 执行一次

  • send : send 会传送一个值给 yield 关键字, 赋值给 yield 左边的的变量名, 再执行等同于 next 的功能继续执行下面的代码

  • close : 当使用 close 时, 会关闭生成器, 无法在进行迭代取值, 取值则报 StopIteration 异常

  • 喂狗示例: ps : “dog.send(None)” 的效果等于 “next(dog)

def eat(name):
    print('%s start eat' %name)
    while True:
        food = yield 1  # yield 接收 send 传过来的值赋值给 food
        print('%s start eat %s'%(name,food))
​
dog = eat('派大星的狗')   # 获得生成器next(dog)                # 派大星的狗 start eat
dog.send("淘小欣")      # 派大星的狗 start eat 淘小欣  (send 自带 next 的执行功能)
dog.close()              # 关闭这个生产器 (无法在进行取值)
next(dog)                # 再次取值跑出异常 "StopIteration"