yield和生成器是什么?

布啦豆 7313

yield在Python里面也是一个较难懂的奇技淫巧,和return类似,但是和return有天差地别的不同

在函数中使用return,会返回值,并且结束函数的运行,如下所示:

def cf(x):
    return x*x

print(cf(8))

打印结果是64,返回了一个数值

如果把return改成yield,则返回的<generator object cf at 0x000001A6621F5570>,一个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和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
<generator object <genexpr> 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)
<ipython-input-6-0b5056469c9c> in <module>()
----> 1 next(r)

StopIteration:

yield的循环顺序:

  • 第一次开始,从函数的第一行代码开始执行,到yield语句停止
  • 第二次开始,从上次停止的yield语句的下一行开始执行,然后再次运行到yield语句停止
  • 依次往后,每次都是到yield停止,以及yield的下一行代码开始执行
  • 直到循环的结束

循环从0、1、2结束之后,就报错了。因为循环只有0、1、2。next()函数在没有数据出来就会报错,所以一般不使用next(),用for循环

yield执行顺序

然后再试试for吧

yield执行和循环

for循环成功输出,且无报错,但是None是什么情况呢?

因为 yield 和 return 一样,都会返回一个值,而yield语句是yield print("Yield", i),没有返回任何数据,只是打印,所以会打印None。

这里再修改下代码,看下运行结果图:

yield剔除None

None没了,取而代之的是数字,也就是当前循环中 i 的值

最后一个问题:那在上面的截图中,next(r)怎么没有None?

因为上面的代码没有输出,所以不会显示输出,有输出才会显示Out[这里是数字]: 这里是值附上最后一张截图

yield最后一张截图

版权声明:允许转载,转载请注明出处 —— 《Python3教程》: yield和生成器是什么?

Copyright @2016-2017 | 赣ICP备16003025号