• Day10 python高级特性-- 生成器 Generator


    列表生成式可以创建列表,但是受内存限制,列表容量时有限的,创建一个巨量元素的列表,不仅占用很大的存储空间,当仅仅访问前几个元素时,后面的绝大多数元素占用的空间都被浪费了。
    如果list的元素可以按照算法推算出来,那么就可以在循环的过程中不断推算出后面的元素,这样就不必创建完整的list,从而节省大部分空间。
    这种一边循环一边计算的机制,在Python中称为生成器:Generator。
    Python可以简单的把列表生成式改成generator,也可以通过函数实现复杂逻辑的generator。
    创建生成器
        方法一: 把一个列表生成式的[] 改成(),就创建了一个生成器。
       
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    >>> a = [ x * x for x in range(1,10) ]
    >>> a       #a是一个list
    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> a = ( x * x for x in range(1,10) )
    >>> a       #a是一个generator
    <generator object <genexpr> at 0x7f2523f8bdb0>
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        打印generator中的元素,可使用next() 函数获得generator的下一个返回值。
       
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    >>> next(a)
    1
    >>> next(a)
    4
    ……
    ………………
    >>> next(a)
    64
    >>> next(a)
    81
    >>> next(a)     #计算到最后一个元素后,没有更多的元素时,就会抛出错误
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    StopIteration
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        使用next()函数获取generator元素的方式并不好用,正确的方法是使用for循环,generator也是可迭代对象。
       
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    >>> a = ( x * x for x in range(1,10) )
    >>> for n in a:
    ...     print(n)
    ...
    4
    9
    16
    25
    36
    49
    64
    81
    >>> 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     
    但是当推算算法比较复杂时,for循环可能无法实现算法,可以用函数来实现。
       
    斐波那契数列中,除第一个和第二个数之外,任意一个数字都可以由前两个数相加得到。这个逻辑用列表生成式写不出来,但是用函数会很容易:
       
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    >>> def fb(long):
    ...                                 #L = []
    ...     n, a, b = 0, 0, 1
    ...     while n < long:
    ...             print(b)        #L.append(b)
    ...             a, b = b, a + b
    ...             n = n + 1
    ...     return 'End'                 #return print(L)
    ...
    #>>> fb(20)
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]         #注释部分是直接生成一个list
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
               
                在这里注意一下,赋值语句的逻辑
                    a, b = b, a + b
                    => t = ( b, a + b )
                         a = t[0]
                         b = t[1]
       
        方法二: 既然生成了list,就距离generator很近了。要把fb()函数变成generator,只需要把print(b) 改为 yield b 就可以了:
       
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    >>> def fb2(long):
    ...     n, a, b = 0, 0, 1
    ...     while n < long:
    ...             yield b
    ...             a, b = b, a + b
    ...             n = n + 1
    ...     return 'End'
    ...
    >>> fb2(100)
    <generator object fb2 at 0x7f2523f8bd58>  
    >>> a = fb2(100)
    >>> next(a)
    1
    >>> next(a)
    1
    >>> next(a)
    2   
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        如果一个函数定义中包含了 yield 关键字,那么这个函数不再是一个普通函数,而是一个generator。
       
    了解一下yield
        先做一个测试函数
                >>> def test():
                ...     print(1)
                ...     yield 1
                ...     print(2)
                ...     yield 2
                ...     print(3)
                ...     yield 3
                ...
                >>> o = test()
                >>> next(o)
                1
                1
                >>> next(o)
                2
                2
                >>> next(o)
                3
                3
                >>> next(o)
                Traceback (most recent call last):
                  File "<stdin>", line 1, in <module>
                StopIteration
        可以看到,在generator执行中,遇到yield就中断了,下次又会接着继续执行。当执行了3次之后,已经没有更多yield可以执行时,再次调用next() 就会抛出错误。
        因此在正常循环调用yield过程中,需要给循环设置一个条件来退出循环,不然就会产生一个无限的数列出来。
        同样的,在改成generator后,基本上不会用next()来获取下一个返回值,而是用for循环来迭代:
              >>> for i in fb2(10):
                ...     print(i)
                ...
                1
                1
                2
                3
                5
                8
                13
                21
                34
                55
        但是for循环迭代generator时,拿不到generator的return语句的返回值。(参见fb2()函数定义内容,与上面对fb2() 进行for迭代的结果)
        如果要拿到返回值,就必须捕获StopIteration错误,返回值包含在StopIteration的Value中:
              >>> def fb2(long):
                ...     n, a, b = 0, 0, 1
                ...     while n < long:
                ...             yield b
                ...             a, b = b, a + b
                ...             n = n + 1
                ...     return 'End'
                ...
               
                >>> b = fb2(10)
                >>> while True:
                ...     try:
                ...             x = next(b)
                ...             print('b:', x)
                ...     except StopIteration as err:
                ...             print('Generator return value:',err.value)
                ...             break
                ...
                b: 1
                b: 1
                b: 2
                b: 3
                b: 5
                b: 8
                b: 13
                b: 21
                b: 34
                b: 55
                Generator return value: End   

    练习: 输出杨辉三角形。
       
    思路:
        每一行的首尾都是1,
        每一行去掉首尾的1之后会发现,n[1] = up[0] + up[1] , n[2] = up[1] + u[p2] ,也就是 n = up[a] + up[a + 1] 
     
    def triangles(n):
        a = [1]
        m = 0
        while n > m:
            yield a
            a = [1] + [a[i] + a[i + 1] for i in range(len(a)-1)] + [1]
            m = m + 1
        return  
       
    for i in triangles(7):
        print(i)
    [1]
    [1, 1]
    [1, 2, 1]
    [1, 3, 3, 1]
    [1, 4, 6, 4, 1]
    [1, 5, 10, 10, 5, 1]
    [1, 6, 15, 20, 15, 6, 1]   
  • 相关阅读:
    Spring boot --- Spring Oauth(三)
    Spring boot --- Spring Oauth(一)
    JavaScript 学习(一)
    Springboot --- Spring Security (一)
    HTML 学习笔记 JavaScript (节点)
    HTML 学习笔记 JavaScript(事件)
    HTML 学习笔记 JavaScript (DOM)
    HTML 学习笔记 CSS3 (多列)
    HTML 学习笔记 CSS3(Animation)
    HTML 学习笔记 CSS3(过度 transition)
  • 原文地址:https://www.cnblogs.com/konggg/p/9098868.html
Copyright © 2020-2023  润新知