yield在Python里面也是一个较难懂的奇技淫巧,和return类似,但是和return有天差地别的不同
在函数中使用return,会返回值,并且结束函数的运行,如下所示:
def cf(x):
return x*x
print(cf(8))
打印结果是64,返回了一个数值
如果把return改成yield,则返回的
,一个generator生成器对象。那么,问题来了....
第一个问题:什么是生成器?
生成器可以理解成制作数据的机器。
在前面的函数学习中,函数可以理解成操作的集合,丢入一个参数,例如8,经过函数的代码执行处理,返回了64。从输入到输出,可以把函数想象成一台机器。
没有yield的函数,调用会直接执行,就像直接启动一台机器,输入数据,机器执行,然后输出数据,完成。
有yield的函数,调用之后不会直接运行,而是给你一个包装了数据的机器。现在你得到的不是数据,而是可以输出数据的机器。
第二个问题:生成器怎么用?
启动生成器很简单,但是Python3和2版本有差异:
>>> def cf(x):
yield x*x
>>> dec = cf(3)
>>> next(dec)
9
上面代码中,定义一个从cf(3)中拿到生成器,并赋值给dec,然后调用next()函数,拿到结果
下面是Python2版本的next函数使用
Python2版本中:
dec.next()
但是一般很少用next()函数。这里的值只有一个,如果继续用next()函数会报错,如下截图:
第三个问题:既然这么麻烦,为何还要用yield关键字呢?
给你一台输入沙子生产娃娃的机器好?还是直接给你有限个数的娃娃好?
其实这是授人以鱼和授人以渔的概念,不是授人以鱼不如授人以渔。饿的快死了,鱼能救你,渔不行;距离死亡还有几十年,渔可以救你鱼不行。
上面的简单示例,只是对一个数据进行测试。如果数据非常多呢?看下面的示例:
问题描述:计算从1到1亿的数字总和,Python3内置sum()函数,括号内放入可迭代数据就行
方法一【慎用,过于消耗内存导致电脑卡,不仅仅是卡顿,是卡的死的那种】:
>>> max_list = [i for i in range(100000000)]
>>> len(max_list)
>>> sum(max_list)
方法二【几乎没多少内存消耗】:
>>> max_list = (i for i in range(100000000))
>>> max_list
at 0x000001E985B06E60>
>>> sum(max_list)
4999999950000000
第一种方法,是列表生成式,直接生成一个一亿元素的列表【如果要执行,请时刻关注内存,别等到卡死再想起关闭程序,那就晚了】。
第二种方法,是得到一个生成列表的生成器,生成器有可迭代性质,所以可以用sum()函数直接计算总和。
第四个问题:yield的运行规则是什么样的?
现在就用一个示例来讲讲,生成器是怎么样运行的.....
先上示例:
>>> def run_order():
for i in range(3):
print("Prever", i)
yield print("Yield", i)
print("Next", i)
>>> r = run_order()
>>> next(r)
Prever 0
Yield0
>>> next(r)
Next 0
Prever 1
Yield 1
>>> next(r)
Next 1
Prever 2
Yield 2
>>> next(r)
Next 2
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
in ()
----> 1 next(r)
StopIteration:
yield的循环顺序:
- 第一次开始,从函数的第一行代码开始执行,到yield语句停止
- 第二次开始,从上次停止的yield语句的下一行开始执行,然后再次运行到yield语句停止
- 依次往后,每次都是到yield停止,以及yield的下一行代码开始执行
- 直到循环的结束
循环从0、1、2结束之后,就报错了。因为循环只有0、1、2。next()函数在没有数据出来就会报错,所以一般不使用next(),用for循环
然后再试试for吧
for循环成功输出,且无报错,但是None是什么情况呢?
因为 yield 和 return 一样,都会返回一个值,而yield语句是yield print("Yield", i)
,没有返回任何数据,只是打印,所以会打印None。
这里再修改下代码,看下运行结果图:
None没了,取而代之的是数字,也就是当前循环中 i 的值
最后一个问题:那在上面的截图中,next(r)怎么没有None?
因为上面的代码没有输出,所以不会显示输出,有输出才会显示Out[这里是数字]: 这里是值
。附上最后一张截图:
Python3更多教程--传送门