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 ..
上一篇 下一篇

猜你喜欢

热点阅读