函数和常用模块【day05】:生成器并行计算(五)

时间:2022-06-13 09:28:48

本节内容

1、概述

2、生成器执行原理

3、send()和__next__()方法的区别

4、yield实现并行效果

一、概述

  之前只是介绍生成器,那有些同学就说了,这个生成器除了能节省资源,提高工作效率,但是我们再哪些场景下可以用呢?在哪些地方可以体现出它的价值呢?下面我们来逐一解答这些疑问。

二、生成器执行原理

1、执行原理

1
2
3
4
5
6
7
8
9
10
11
12
13
def consumer(name):
    print("%s 准备吃包子啦!"%name)
 
    while True:
        baozi = yield
 
        print("包子[%s]来了,被[%s]吃了"%(baozi,name))
 
= consumer("zhangqigao")
c.__next__()
 
#输出
zhangqigao 准备吃包子啦!

如果我再加一个__next__()方法会有什么效果?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def consumer(name):
    print("%s 准备吃包子啦!"%name)
 
    while True:
        baozi = yield
 
        print("包子[%s]来了,被[%s]吃了"%(baozi,name))
 
= consumer("zhangqigao")
c.__next__()
c.__next__()
 
#输出
zhangqigao 准备吃包子啦!
包子[None]来了,被[zhangqigao]吃了

很明显,第一种情况没有执行"print("包子[%s]来了,被[%s]吃了"%(baozi,name))",这段代码,接下来我们就来调试一下。

2、调试

第一步:生成一个生成器

函数和常用模块【day05】:生成器并行计算(五)

第二步:执行第一个__next__()方法进入函数,执行到yield时中断,把返回值返回给baozi这个变量:

函数和常用模块【day05】:生成器并行计算(五)

第三步:开始执行下面的程序,也就执行到了第二个__next__()方法,直接跳转到yield这边,继续上一次的中断往下执行,这样就执行了yield下面的程序,当再次执行到yield关键字时,则继续中断,并且把返回值赋给baozi关键字,如果下面没有其他程序,则程序结束。

函数和常用模块【day05】:生成器并行计算(五)

小结:

  1. 用yield做生成器,你想把什么返回到外面,你就把yield关键字放在那里。
  2. yield其实是保留了函数的中断状态,返回当前的值。
  3. 如果yield没有返回值,就返回一个空值None

三、send()和__next__()方法的区别

1、send()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def consumer(name):
    print("%s 准备吃包子啦!"%name)
 
    while True:
        baozi = yield
 
        print("包子[%s]来了,被[%s]吃了"%(baozi,name))
 
= consumer("zhangqigao")
c.__next__()  #不使用__next__()方法会报错
b1 = "肉松馅"
c.send(b1)   #调用yield,同时给yield传一个值
b2 = "韭菜馅"
c.send(b2)
 
#输出
zhangqigao 准备吃包子啦!
包子[肉松馅]来了,被[zhangqigao]吃了
包子[韭菜馅]来了,被[zhangqigao]吃了

从上面可以看出send()和__next__()方法的区别:

  1. __next__()只是调用这个yield,也可以说成是唤醒yield,但是不不会给yield传值。
  2. send()调用这个yield或者说唤醒yield同时,也活给yield传一个值。
  3. 使用send()函数之前必须使用__next__(),因为先要中断,当第二次调用时,才可传值。

为什么给消费者传值时,必须先执行__next__()方法?

因为如果不执行一个__next__()方法,只是把函数变成一个生成器,你只有__next__()一下,才能走到第一个yield,然后就返回了,调用下一个send()传值时,才会发包子。

四、yield实现并行效果

yield还有一个更强大的功能,就是:单线程实现并发效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import time
 
def consumer(name):
    print("%s 准备吃包子啦!"%name)
 
    while True:
        baozi = yield
 
        print("包子[%s]来了,被[%s]吃了"%(baozi,name))
 
 
def producer(name):
    = consumer("A")
    c2 = consumer("B")
    c.__next__()
    c2.__next__()
    print("老子准备吃包子啦!")
    for in range(10):
        time.sleep(1)
        print("做了一个包子,分两半")
        c.send(i)
        c2.send(i)
 
producer("zhangqigao")

这个是生产者消费者模式,这个也就是后面协程的效果,这个我们后续再讲,先了解一下,生成器可以实现并发效果,极大的提高程序的运行效率。