zoukankan      html  css  js  c++  java
  • 迭代器和生成器

    # 最简单的生成器
    l1 = [2, 4, 5, 6]
    # 利用生成器实现生成一个新列表,元素等于l内的每个元素+1
    l2 = [x+1 for x in l1]
    print(type(l2))  # 结果:<class 'list'>
    print(l2)  # 结果:[3, 5, 6, 7]
    
    

    # 因为生成器会根据逻辑遍历可迭代对象(此处是l1)生成新的对象。这本身没什么问题,也符合我们的要求
    # 但是如果可迭代对象的内容非常庞大例如:

    # l3 = [x+1 for x in range(1000000)]
    # print(l3)  # 直接生成新的对象后,存储下来。而对象在接下来会被调用,所以会保存在内存中

    # 通过上面的例子,我们知道直接使用生成器如果遇到较大的迭代对象,就会占用非常大的内存

    # 下面通过该为迭代器的方法来实现上述例子

    # l4 = (x+1 for x in range(1000000))
    # print(l4)  # 结果是生成器:<generator object <genexpr> at 0x0000000001ED23C8>
    # 通过next(生成器)取值,取到最后一个值的时候,再次取值会报错
    # l5 = (x+1 for x in l1)
    # print(next(l5))  # 结果是:3
    # print(next(l5))  # 结果是:5
    # print(next(l5))  # 结果是:6
    # print(next(l5))  # 结果是:7
    # print(next(l5))  # 此处生成器中已经没有多余的元素可以取了,所以会抛异常StopIteration,结果如下:
    """
    Traceback (most recent call last):
      File "D:/02Project/pacho/15day/st_迭代.py", line 26, in <module>
        print(next(l5))
    StopIteration
    """

    # 为了避免此类情况的发生,可以采用for循环的的方式获取生成器的值,不会抛出异常,例子如下:

    l6 = (x for x in l1)
    print(type(l6))  # 结果是:<class 'generator'>
    for i in l6: print(i)  # 结果是  2  4  5  6

    # 插入:偶然发现了一个错误的写法

    l7 = (x for x in l1)
    for i in l7: print(next(l7))  # 结果是  4  6  原因是 for循环已经在调用next方法了,所以再次print(next(l7))实际是再次取一次值

    # 好了步入正题了,下面看下典型应用——斐波那契数列
    # 这里写一个生成斐波那契数列的函数

    def fei(n):  # 输出前n个数
        x, y, z = 0, 0, 1
        while True:
            if x < n:
                print(z)
                y, z = z, y + z
                x += 1
            else:
                break
        return 'this is res'
    
    
    # 运行一下看看
    fei(5)  # 结果是:  1  1  2  3  5
    fei(8)  # 结果是:  1  1  2  3  5  8  13  21

    # 现在我们不想让他一下输入所有结果,想让他每次调用输出一个结果(不要说什么根据递归重新写个方法,虽然也可行。。。)

    f = fei(5)  # 运行函数fei(5)  输出结果:  1  1  2  3  5 并把函数的返回值'this is res'赋值给f
    print(type(f))  # 结果是:<class 'str'>
    print(f)  # 结果是:this is res

    # 显然上面的操作并没有实现每次取他输出结果的功能,那么我们改一下上述函数,是他由一个普通函数变成生成器

    def fei_1(n):  # 输出前n个数
        x, y, z = 0, 0, 1
        while True:
            if x < n:
                yield z  # 只是在这里吧输出改成了yield
                y, z = z, y + z
                x += 1
            else:
                break
        return 'this is res'
    
    # 再看下输出结果
    f = fei_1(5)  # 运行函数fei_1(5)  没有输出结果,而是把函数变成了一个生成器
    print(f)  # 结果是:<generator object fei_1 at 0x0000000001EA23C8>  可以看到我们得到了一个生成器
    print(type(f))  # 结果是:<class 'generator'>
    print(next(f))  # 结果是:1
    # 我们写个简单的来看下
    def f(n):
        for i in range(n):
            print('for start ...' if i == 0 else 'start start start')
            yield i
            print('for end ...' if i == n - 1 else 'end end end')
        return 'done'
    #
    n = f(3)  # 得到了一个生成器
    print(type(n))  # 结果:<class 'generator'>
    print(n)  # 结果:<generator object f at 0x0000000001EA23C8>
    print(next(n))  # 结果:【for start ...】  【0】
    print(next(n))  # 结果:【end end end】  【start start start】 【1】
    print(next(n))  # 结果:【end end end】  【start start start】 【2】 【for end ...】
    print(next(n))  # 生成器中没有元素,抛异常,导致一个尴尬的结果就是没有获取到函数f的返回值
    """
    Traceback (most recent call last):
      File "D:/02Project/pacho/15day/st_迭代.py", line 101, in <module>
        print(next(n))  # 结果:【end end end】  【start start start】 【1】
    StopIteration: done
    """

    # 从上面的例子我们就能够知道yield关键字生成器的作用:
    # 1、通过yield关键字把一个函数变成生成器
    # 2、每次调用会执行一次函数,并且执行到yield关键字后暂停
    # 3、再次调用会从yield之后的代码开始执行
    # 4、当生成器执行结束之后,才会返回函数的返回值

    # 下面换一种能够获取到函数返回值的方法

    gen = f(3)
    print(type(gen))
    while True:  # 循环取生成器的元素
        try:
            i = next(gen)  # 取生成器的元素
            print(i)
        except StopIteration as e:  # 当取不到的时候,捕获异常,并得到生成器函数的返回值
            print(e)
            break
    # 结果是
    """
    <class 'generator'>
    for start ...
    0
    end end end
    start start start
    1
    end end end
    start start start
    2
    for end ...
    done
    """

    # 好了,现在或过头来看自定义的生成器函数fei(生成斐波那契额数列,并且有返回值),该怎么调用呢,和上面一样

    ff = fei_1(6)
    while True:
        try:
            print(next(ff))
        except StopIteration as e:  # 顺便加深理解了一下捕捉异常:如果调用某个生成器函数,捕获得到的异常e和e.value均为该函数的返回值
            print(e)
            print(e.value)
            break
    
    # 结果如下:
    """
    1
    1
    2
    3
    5
    8
    this is res
    this is res
    """

    参考:

    前人种树后人乘凉,感谢分享:https://www.cnblogs.com/wj-1314/p/8490822.html

  • 相关阅读:
    [译]Vulkan教程(09)窗口表面
    [译]Vulkan教程(08)逻辑设备和队列
    [译]Vulkan教程(07)物理设备和队列家族
    Linux命令行文本工具
    go语言周边
    go第三方常用包
    Centos6安装gcc4.8及以上版本
    pyenv设置python多版本环境
    Redis慢日志
    PHP-CPP开发扩展(七)
  • 原文地址:https://www.cnblogs.com/wjlv/p/11660743.html
Copyright © 2011-2022 走看看