yield和生成器是什么?

作者: 布拉豆 | 来源:发表于2017-04-27 11:58 被阅读91次

    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和生成器是什么?

        本文链接:https://www.haomeiwen.com/subject/jxjgzttx.html