什么是yield关键字
先看一段代码:
def my_generator(n):
for i in range(n):
yield i
f = my_generator(3)
print(f.__next__())
print(f.__next__())
print(f.__next__())
运行结果:
0
1
2
yield关键字会将一个函数变成一个生成器,生成器和普通函数不同,它不会直接执行函数代码,而需要调用__next__()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
生成器的应用
应用示例1
def read_file(fpath):
BLOCK_SIZE = 20
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return
f = read_file('testfile.txt')
print(f.__next__())
print(f.__next__())
运行结果:
b'FastAPI is a modern,'
b' fast (high-performa'
读取文件,每次读取20个字节
应用示例2
for i in range(1000):
print(i)
会导致生成一个 1000 个元素的 List
for i in xrange(1000):
print(i)
不会生成一个 1000 个元素的 List,而是在每次迭代中返回下一个数值,内存空间占用很小。因为 xrange 不返回 List,而是返回一个 iterable 对象
yield语句的用法总结
yield的一般形式为:
temp=yield 表达式(每次迭代要返回的值)
(1)如果要返回确定的值,后面的表达式不可省略,绝大部分情况下我们也不省略,否则只能返回None;
(2)如果使用了send(value),传递进去的那个value回取代那个表达式的值,并且会将传递进去的那个值返回给yield表达式的结果temp,所以如果想在yield后面使用传递进去的那个值,必须要有使用temp,否则无法使用;
send()方法的使用
def my_generator(n):
for i in range(n):
temp = yield i
print(f'我是{temp}')
g = my_generator(5)
print(next(g)) # 输出0
print(next(g)) # 输出1
g.send(100) # 本来输出2,但是传入新的值100,改为输出100
print(next(g)) # 输出3
print(next(g)) # 输出4
运行结果:
0
我是None
1
我是100
我是None
3
我是None
4
从上面可以看出yield语句与普通函数的return语句的区别在哪里了,主要集中在以下几点
(1)return 不能写成“temp=return xxxx”的形式,会提示语法错误,但是yield可以写成“temp=yield xxxx”的形式;
(2)普通函数return后面的语句都是不会再执行的,但是yield语句后面的依然会执行,但是需要注意的是,由于“延迟加载”特性,yield后面的代码并不是在第一次迭代的时候执行的,而是第二次迭代的时候才执行第一次yield后面没有执行的代码。也正是这个特性,构成了yield为什么是实现协程的最简单实现。
(3)使用send()方法传进去的值,实际上就是yield表达式返回的值,这就是为什么前面每次输出print(temp)都打印出None,因为没有send值,所以temp为None,但是send(100)之后却打印100,因为此时temp就是100了。
可以看到迭代器并没有输出2,那是因为g.send(100)是有返回值的,返回值内容就是2
send(arg)方法总结:
(1)它的主要作用是,当我需要手动更改生成器里面的某一个值并且使用它,则send发送进去一个数据,然后保存到yield语句的返回值,以提供使用
(2)send(arg)的返回值就是那个本来应该被迭代出来的那个值。这样既可以保证我能够传入新的值,原来的值也不会弄丢
网友评论