Python 中的 yield
2020-06-11 本文已影响0人
teletian
yield 优化内存占用
有这样一个例子:
def return_test():
return [i for i in range(10)]
list = return_test()
print(list)
return_test() 返回一个 list,然后打印这个 list。
貌似没有什么问题。
那么再来看下面这个例子:
def return_test():
return [i for i in range(100000)]
list = return_test()
print(list)
和上面例子唯一不同的是 return_test() 返回的 list 长度变成了 10 万,数据量变多了。
由于 list 是要存储在内存中的,遇到海量的数据就会非常的占用内存。
这时候 yield 就派上用场了。
def yield_test():
print("yield_test start.")
for i in range(10):
print("for loop start", i)
yield i
print("for loop end", i)
print("yield_test end.")
generator = yield_test()
print(type(generator))
for item in generator:
print("generator", item)
带有 yield 的函数不再是一个普通函数,而是一个生成器 generator。
generator 和 list 一样,可以迭代。
其实 yield 可以理解为和 return 一样,只是会记住返回的位置,下次迭代的时候会从返回的位置开始继续往下执行。
generator = yield_test()
执行完之后 yield_test 方法并没有执行,而是等到 generator 被迭代的时候才开始执行。
generator 有一个 next() 方法,每次被迭代都是调用 next() 方法,也就是每次都执行一次 for 循环。
由于不是把所有值先准备好放内存中,而是每次迭代的时候生成,所以内存占用大大的减少了。
yield 实现协程
典型的例子就是生产者消费者
def consumer():
print("--【consumer】start --")
response = None
while True:
print('【consumer】中断执行,保存上下文')
print("--> 切换到 produce")
# n = yield response 分两步。1. yield response 2. n =
# 第一步 yield 把值返回给生产者之后即停止
# 当调用 c.send(n) 继续执行时接收 send 的参数值赋给 n
n = yield response
print('【consumer】获取上下文,继续往下执行')
if not n:
return
print(f"【consumer】: 消费 {n} ..")
response = "OK"
def produce(c):
print("--【produce】start --")
print("【produce】启动生成器...")
print("--> 切换到 consumer")
c.send(None) # 启动生成器必须传入 None
print("【produce】启动生成器成功")
n = 0
while n < 5:
n += 1
print(f"【produce】生产 {n} ..")
print("--> 切换到 consumer")
# 把 n 发送给消费者,n = yield response 这段代码的 n 会接收发送的值
# r 即为 yield response 的 response 的值
r = c.send(n)
print(f"【produce】消费者 return {r} ..")
# 关闭生成器
c.close()
if __name__ == "__main__":
c = consumer()
produce(c)
consumer() 获取生成器。
调用 send(None) 启动生成器,consumer 方法的代码开始执行,遇到 yield 挂起 consumer 继续执行 produce。
调用 send(n) 从上次挂起的地方继续执行 consumer 方法的代码。
调用 close() 关闭生成器。
运行结果如下:
--【produce】start --
【produce】启动生成器...
--> 切换到 consumer
--【consumer】start --
【consumer】中断执行,保存上下文
--> 切换到 produce
【produce】启动生成器成功
【produce】生产 1 ..
--> 切换到 consumer
【consumer】获取上下文,继续往下执行
【consumer】: 消费 1 ..
【consumer】中断执行,保存上下文
--> 切换到 produce
【produce】消费者 return OK ..
【produce】生产 2 ..
--> 切换到 consumer
【consumer】获取上下文,继续往下执行
【consumer】: 消费 2 ..
【consumer】中断执行,保存上下文
--> 切换到 produce
【produce】消费者 return OK ..
【produce】生产 3 ..
--> 切换到 consumer
【consumer】获取上下文,继续往下执行
【consumer】: 消费 3 ..
【consumer】中断执行,保存上下文
--> 切换到 produce
【produce】消费者 return OK ..
【produce】生产 4 ..
--> 切换到 consumer
【consumer】获取上下文,继续往下执行
【consumer】: 消费 4 ..
【consumer】中断执行,保存上下文
--> 切换到 produce
【produce】消费者 return OK ..
【produce】生产 5 ..
--> 切换到 consumer
【consumer】获取上下文,继续往下执行
【consumer】: 消费 5 ..
【consumer】中断执行,保存上下文
--> 切换到 produce
【produce】消费者 return OK ..