之前一直感觉对生成器的理解不够,想再专门写一篇深入理解一下生成器:
1.生成器简介:
python中,含有yield关键字的对象就是一个生成器,每次调用next方法时会执行到yield后面的语句,然后返回yield后面代码块的执行结果,并挂起。
next方法
def foo():
a = yield 1 # bar_a是语句块(yield 1)的返回值,默认为None
b = yield a
yield "最后一个值,再迭代就要报StopIteration了"
f = foo() # 创建生成器,此时没有执行foo()里的任何语句
print(next(f)) # 从foo()里进入,一直执行到(yield 1)处,此时变量a还没有创建,直接返回yield后面的代码块
print(next(f)) # 先将语句块(yield 1)的返回值赋值个a,此时a的值是None。
# 然后执行到语句块(yield a),b也还没有被创建
print(next(f))
执行结果:
1
None
最后一个值,再迭代就要报StopIteration了
第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句后,跳出生成器函数,并将yield关键字后面的内容返回给调用者;
第二个next调用从yield语句的下一句语句开始执行,直到遇到yield或者元素迭代完毕。
而且我们可以看到a和b是语句yield 1和yield a的返回值,注意:不是生成器的返回值。
这里有个比较绕的地方,我们用a = yield 1做分析:
1是生成器的返回值。因为生成器返回yield后面的代码块
a是语句yield 1的返回值,这就好比我们写
a = print('my lover')
print('a的值是:', a)
输出:
my lover
a的值是: None
send方法
def foo():
a = yield 1
b = yield a
yield "最后一个值,再迭代就要报StopIteration了"
f = foo()
print(f.send(None))
print(f.send("my lover"))
print(next(f))、
输出结果:
1
my lover
最后一个值,再迭代就要报StopIteration了
这里f.send(None)是初始化生成器,和next(f)的效果一模一样。但是不推荐这么写,因为不规范。如果直接写send不输入None或者不执行Next,就会报错;
注意输出的第二行是字符串my lover,而不是None。这是因为send函数带有一个参数,这个参数会覆盖yield 1语句的返回值,也就是不管yeild后面进行了什么运算都会被覆盖,也就是a的值现在不是None了。
a = yield 1这句话是从右往左执行的。当第一次send(None)时,启动生成器,从生成器函数的第一行代码开始执行,直到第一次执行完yield后,跳出生成器函数并返回结果1。这个过程中,a一直没有定义。
运行到send('my love’r)时,进入生成器函数,此时,将yield a看做一个整体,赋值给它并且传回。此时即相当于把"my lover赋值给b,即send和next相比,只是开始多了一次赋值的动作,其他运行流程是相同的
实际上next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。
第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值
send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互。
举例:
def test():
i=0
while i<5:
temp=yield i*2
print(temp)
i=i+1
a=test() # 生成器对象 ,里面都是存储 i*i的值
print("next start")
print(next(a)) #取第一个i*i值
print("__next__ start")
print(a.__next__()) # 换种方法 取第二个 i*i 值
print("send start")
print(a.send("hahha")) # 这个send 方法 可以传入参数, 并且 yield 进行接受,说明yield 既可以存储 i*i的值 ,也可以接受参数并赋值给temp
print(a.send("heihei"))
运行结果:
next start
0
__next__ start
None
2
send start
hahha
4
heihei
6
总结: 目前为止 我们可以取生成器的方法有三种 1.next(),2.( next) 3. send(arg) 这个不但可以取值,也可以传递参数
大家感觉不停的用next()方法进行取值,非常的麻烦,大家还记得吗,
使用for循环输出生成器结果:
生成器也是迭代器, 迭代器既可以用next()取值,也可以用for循环(可迭代特性)
def test():
i=0
while i<5:
temp=yield i*2
print(temp)
i=i+1
a=test() # 生成器对象 ,里面都是存储 i*i的值
for i in a:
print(i)
运行结果:
0
None
2
None
4
None
6
None
8
None