• python学习之---生成器


    通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含1000万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

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

    要创建一个generator,有很多种方法。

    第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

    1. 简单的列表操作:

    1 >>> list1 = [x*x for x in range(10)]
    2 >>> list1
    3 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    4 >>> list2 = [2*x for x in range(12)]
    5 >>> list2
    6 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
    7 >>> 

    2. 生成器操作:

    1 >>> list3 = (x*x for x in range(10))
    2 >>> list3
    3 <generator object <genexpr> at 0x7f302fdb6cd0>
    4 >>> list1
    5 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    6 >>> list2
    7 [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
    8 >>> 

    如代码所示,list1和list2是一个列表,而list3是一个generator,在任意时刻我们可以随便打印出list1和list2的列表元素,但是我们怎么才可以打印出list3的元素呢?,在这里generator提供一个打印自身元素的函数,next()

     1 >>> list3
     2 <generator object <genexpr> at 0x7f302fdb6cd0>
     3 >>> list3.next()
     4 0
     5 >>> list3.next()
     6 1
     7 >>> list3.next()
     8 4
     9 >>> list3.next()
    10 9
    11 >>> list3.next()
    12 16
    13 >>> list3.next()
    14 25
    15 >>> list3.next()
    16 36
    17 >>> list3.next()
    18 49
    19 >>> list3.next()
    20 64
    21 >>> list3.next()
    22 81
    23 >>> list3.next()
    24 Traceback (most recent call last):
    25   File "<stdin>", line 1, in <module>
    26 StopIteration
    27 >>> 

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

    使用next方法虽然可以计算出生成器的值,但是,实在是太不人性化了,在此,还有没有更好的方法来输出生成器的值呢?方法肯定是有的,那就是采用for迭代输出:

     1 >>> list4 = (x*x for x in range(10))
     2 >>> list3
     3 <generator object <genexpr> at 0x7f302fdb6cd0>
     4 >>> for n in list4:
     5 ...     print n
     6 ... 
     7 0
     8 1
     9 4
    10 9
    11 16
    12 25
    13 36
    14 49
    15 64
    16 81
    17 >>> 

    所以,我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。

    generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

    比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

    1, 1, 2, 3, 5, 8, 13, 21, 34, ...

    斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

     1 >>> 
     2 >>> def fibla(max):
     3 ...     n,a,b = 0,0,1
     4 ...     while n < max:
     5 ...         print b
     6 ...         a,b = b,a+b
     7 ...         n = n + 1
     8 ... 
     9 >>> fibla(8)
    10 1
    11 1
    12 2
    13 3
    14 5
    15 8
    16 13
    17 21
    18 >>> 

    观察之后发现,fibla函数其实是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

    也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print b改为yield b就可以了:

     1 >>> def fibla(max):
     2 ...     n,a,b = 0,0,1
     3 ...     while n < max:
     4 ...         print b
     5 ...         a,b = b,a+b
     6 ...         n = n + 1
     7 ... 
     8 >>> fibla(8)
     9 1
    10 1
    11 2
    12 3
    13 5
    14 8
    15 13
    16 21
    17 #上下对比
    18 >>> def fibla(max):
    19 ...     n,a,b = 0,0,1
    20 ...     while n < max:
    21 ...         yield b
    22 ...         a,b = b,a+b
    23 ...         n = n +1
    24 ... 
    25 >>> fibla(8)
    26 <generator object fibla at 0x7f302fd5ca00>
    27 >>> 

    这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:

    这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

     1 >>> def fibla(max):
     2 ...     n,a,b = 0,0,1
     3 ...     while n < max:
     4 ...         yield b
     5 ...         a,b = b,a+b
     6 ...         n = n +1
     7 ... 
     8 >>> fibla(8)
     9 <generator object fibla at 0x7f302fd5ca00>
    10 >>> for x in fibla(8):
    11 ...     print x
    12 ... 
    13 1
    14 1
    15 2
    16 3
    17 5
    18 8
    19 13
    20 21
    21 >>> 
  • 相关阅读:
    php多态简单示例
    php接口
    PHP的两种表单数据提交方式
    PHP操作数据库
    51nod 1575 Gcd and Lcm
    51 nod 1297 管理二叉树
    51 nod 1628 非波那契树
    51 nod 1211 数独 DLX
    51nod:1689 逛街
    51 nod 1203 JZPLCM
  • 原文地址:https://www.cnblogs.com/blogofwyl/p/4294616.html
Copyright © 2020-2023  润新知