zoukankan      html  css  js  c++  java
  • python学习之【第十三篇】:Python中的生成器

    1.为什么要有生成器?

    在Python中,通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。那么此时聪明的你肯定会这么想:有没有一种机制,当我们想要创建列表时,不要直接先把列表中元素全部创建出来,而是把列表元素生成的方法给我,当我要使用哪几个元素的时候我再生成,现做现卖,用多少做多少,这样多好。其实,这些前人们早就替我们想到了,所以,生成器应运而生。

    2.什么是生成器?

    如果列表元素可以按照某种算法推算出来,这样我们就不必创建出完整的列表,而是在循环的过程中不断推算出后续的元素,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器generator

    3.如何创建生成器?

    要创建一个生成器generator,有很多种方法。我们介绍两种常用的方法:

    1. 简单生成器。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个生成器generator
    2. 生成器函数。如果推算的算法比较复杂,用类似列表生成式无法实现的时候,还可以用函数来实现,这就是生成器函数。

    4. 生成器表达式

    只要把一个列表生成式的[ ]改成( ),就创建了一个生成器表达式generator

    创建Lg的区别仅在于最外层的[ ]( )L是一个list,而g是一个generator

    我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

    如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

    generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

    因为generator也是可迭代对象,所以可以使用for循环来迭代:

    我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

    5. 生成器函数

    在上述的简单生成器中,元素推算的算法仅仅是x*x,比较简单。但是如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,我们可以用函数来实现。这个定义元素推算算法的函数我们称之为生成器函数。

    生成器函数与普通函数有一个很重要的区别,那就是:生成器函数中需要return的地方通通使用yield来替换。

    yield关键字

    yield关键字,其作用和return的功能差不多,就是返回一个值给调用者,只不过有yield的函数返回值后函数依然保持调用yield时的状态,当下次调用的时候,在原先的基础上继续执行代码,直到遇到下一个yield或者满足结束条件结束函数为止。

    我们可以看以下例子:

    首先定义一个生成器函数,调用该生成器函数时,返回一个生成器对象generator,然后用next()函数不断获得下一个返回值,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(g)就报错。

    同样的,使用生成器函数,我们基本上也不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

    def test():
        print('step1')
        yield 1
        print('step2')
        yield 2
        print('step3')
        yield 3
    
    
    g = test()
    
    for i in g:
        print(i)
    # 输出
    # step1
    # 1
    # step2
    # 2
    # step3
    # 3
    

    6.生成器函数与普通函数的区别

    生成器函数与普通函数主要有以下不同:

    • 生成器函数包含一个或者多个yield
    • 当调用生成器函数时,函数将返回一个对象,但是不会立刻向下执行;
    • __iter__()__next__()方法等是自动实现的,所以我们可以通过next()方法对对象进行迭代;
    • 一旦函数被yield,函数会暂停,控制权返回调用者;
    • 局部变量和它们的状态会被保存,直到下一次调用;
    • 函数终止的时候,StopIteraion会被自动抛出;

    7.应用

    咋一看,生成器好像没有什么用啊,其实不然,很多情况下元素是无穷无尽的,例如斐波那契数列,杨辉三角等,这些元素都是不能被穷举的,所以我们无法将所有元素都放到一个列表里,只能是保存元素推算的算法,再进行逐个推算,这就是生成器的好处。

    例如生成杨辉三角的代码,把每一行看做一个list,试写一个generator,不断输出下一行的list

              1
             / 
            1   1
           /  / 
          1   2   1
         /  /  / 
        1   3   3   1
       /  /  /  / 
      1   4   6   4   1
     /  /  /  /  / 
    1   5   10  10  5   1
    
    # 杨辉三角
    def triangles():
        old_list = []
        new_list = []
        while True:
            length = len(old_list)
            if length == 0:
                new_list.append(1)
            else:
                for item in range(length + 1):
                    if item == 0:
                        new_list.append(1)
                    elif item == length:
                        new_list.append(1)
                    else:
                        tmp = old_list[item - 1] + old_list[item]
                        new_list.append(tmp)
            yield new_list
            old_list = new_list.copy()
            new_list.clear()
    

    (完)

  • 相关阅读:
    update mysql row (You can't specify target table 'x' for update in FROM clause)
    MySQL中使用group by 是总是出现1055的错误
    centos7下查看mysql配置文件适用顺序
    mysql中EXPLAIN 的作用
    查看mysql库大小,表大小,索引大小
    mysql互换表中两列数据方法
    mysql在建表语句中添加索引
    使用vue-lazyload实现图片懒加载
    vue使用jsonp获取数据,开发热卖推荐组件
    vue实现首页导航面板组件
  • 原文地址:https://www.cnblogs.com/wangjiachen666/p/9758860.html
Copyright © 2011-2022 走看看