zoukankan      html  css  js  c++  java
  • python 生成器

    生成器可以理解为一种的数据结构,将算法保存,每次计算并返回一个结果,实现了迭代器协议,生成器也是迭代器

    生成器有两种表现形式,1)生成器表达式;2)生成器函数

    1、生成器表达式

    说到生成器表达式,就得先说一下列表推导式   [i for i in range(10)]   ,生成器表达式,就是将 [ ]  改为 (),区别如下所示

     1 >>> b = [i for i in range(20)]
     2 >>> b
     3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
     4 
     5 >>> b = (i for i in range(20000))
     6 >>> b
     7 <generator object <genexpr> at 0x00000185EC6104F8>
     8 >>> next(b)
     9 0
    10 >>> next(b)
    11 1
    12 >>> next(b)
    13 2

    生成器表达式优点:省内存,一次只计算返回一结果。 缺点:不知道有几个元素,只能往后遍历,不能向前遍历,且只能整个生成器只能遍历一次

    列表推导式优点:可以通过下标获取元素。 缺点:占用内存大

    2、生成器函数

    生成器函数:在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数

    我们都知道 return 是函数的返回值,yield 也可以将值返回,我们先看一个简单的生成器函数与普通函数之间的差别

     1 >>> def test():
     2 ...     print("_______生成器函数")
     3 ...     yield "返回值1"
     4 ...     yield "返回值2"
     5 ...     yield "返回值3"
     6 ...
     7 >>> test()
     8 <generator object test at 0x000001651BEC0570>
     9 >>> t1 = test()
    10 >>> print(t1.__next__())
    11 _______生成器函数
    12 返回值1
    13 >>> print(t1.__next__())
    14 返回值2
    15 >>> print(t1.__next__())
    16 返回值3
    17 >>> print(t1.__next__())
    18 Traceback (most recent call last):
    19   File "<stdin>", line 1, in <module>
    20 StopIteration

     可以看到在第7行调用test()函数的时候,并没有执行函数,而是返回了一个生成器对象。在第10行执行 t1.__next__() 才真正的执行了函数,返回 "返回值1",并保存当前的生成器函数的状态。

    每执行一次 next(),生成器函数将执行到下一 yield 并将相应的结果返回,直到产生StopIteration异常。

    理解了上述过程,就不难理解生成器表达式的原理,next(t1)  等价于  t1.__next__()

    def test():
        for i in range(10):
            yield i
    
    print(test())
    t2 = test()
    print(next(t2))
    print(next(t2))
    print(next(t2))
    
    t3 = (i for i in range(10))
    print(t3)
    print(next(t3))
    print(next(t3))
    print(next(t3))
    
    #输出结果
    <generator object test at 0x000001A12D8FF570>
    0
    1
    2
    <generator object <genexpr> at 0x000001A12D8FF5E8>
    0
    1
    2

    3、生成器强调,send()

    生成器中还有一个重要的方法  send() 方法,send()  方法可以解决将值传递给生成器函数的问题,具体如下:

     1 >>> def test():
     2 ...     print("____start_____")
     3 ...     y1 = yield "返回1"
     4 ...     print("____生成器函数内:%s" % y1)
     5 ...     y2 = yield "返回2"
     6 ...     print("____生成器函数内:%s" % y2)
     7 ...     yield "返回3"
     8 ...
     9 >>>
    10 >>> t1 = test()
    11 >>> print(t1.__next__())
    12 ____start_____
    13 返回1
    14 >>> print(t1.send("a"))
    15 ____生成器函数内:a
    16 返回2
    17 >>> print(t1.send("b"))
    18 ____生成器函数内:b
    19 返回3

    在这里第一次获取值的时候,不能用send(),否者会报错,如果一定要用send,则传值None,如下:

    >>> t2 = test()
    >>> t2.send("a")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: can't send non-None value to a just-started generator
    >>> t3 = test()
    >>> t3.send(None)
    ____start_____
    '返回1'

    于send()  不是很理解的可以尝试往下看,下边是我的一些个人理解,不是很准确,我觉得这样会比较好理解一些

    我们可以把函数内 第3行   y1 = yield "返回1"  看成两个过程:1)yield "返回1"    2)y1 = receive_from_send()   [并没有这个方法,只是为了理解说明]

    这样就可以看到,当执行 send() 时将参数"a" 传递给生成器函数,并在 1)过程停留,传递过去的值没有语句接收,则会出错

    所以第一次是不能传值给生成器函数,y1接收到的值为下一次遍历遍历生成器接收的值。

    这里也可以也可以在,上上边的代码执行流程可以看出,执行了 14行,y1才会接收到值。

    补充一点:每执行一次 1)next(),2)__next(),3)send(),都会在yield语句停留,并保存当前状态,知道直到产生StopIteration异常,结束遍历。

    通过生成器,实现斐波那契数列

    def fib(n):
        a, b = 0, 1
        while True:
            yield b
            a, b = b, a + b
            if b > n:
                return
    
    
    f1 = fib(100)   #小于 100 的斐波那契数列
    for i in f1:
        print(i,end=" ")
    
    #输出结果
    1 1 2 3 5 8 13 21 34 55 89
  • 相关阅读:
    18-10-11 关于触发器的学习
    18-10-29 关于设计器机器人等安装遇到的问题的解决方法
    18-10-25 全局函数测试总结 创建时间的目录 网页获取数据 写入数据
    18-09-08 关于Linux 的安装遇到的一些小坑
    18-08-27 机器人自动化之页面表格数据的定位拾取
    day 96 关于分页的使用
    day73 母版 中间件
    通过 U 盘启动重装 macOS 系统
    MAMP 环境下为 php 添加 pcntl 扩展
    使用 Composer 安装 Laravel 框架
  • 原文地址:https://www.cnblogs.com/yhongji/p/9471795.html
Copyright © 2011-2022 走看看