一、生成器介绍
什么是生成器( generator
)
-
生成器就是一个自定义迭代器
-
函数体内含有
yield
关键字
为何要用生成器?
-
为了节省内存
创建生成器的两种方式
-
调用
yield
关键字 -
使用生成器表达式
如何用生成器
函数体内但凡出现yield
关键字,调用函数将不会触发函数体代码的运行,而是会返回一个生成器对象,生成器本质就是一个迭代器
二、生成器与yield
如何得到自定义的迭代器
在函数内定义yield
关键字, 调用函数并不会执行完函数体代码, 会返回一个生成器对象(生成器就等同于迭代器)
注意:
一旦函数体内出现yield关键字, 再去调用函数, 就和函数体代码没有关系了, 得到的就是一个生成器, 你的函数体代码的生效, 取决与你的next
示例
def func():
yield 1
yield 2
yield 3
res_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) # 1
res2 = res_generator.__next__()
print(res2) # 2
res3 = res_generator.__next__()
print(res3) # 3
# 补充: 之前我们使用的len()功能, 内部其实最后调用的还是内置的__len__()方法
res = len('aaa')
print(res) # 3
res = '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 的区别
-
相同点
-
在返回值的及的角度,用法都一样
-
-
不同点
-
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"