• Python高级特性——生成器(generator)


    通过上节的学习,我们知道使用列表生成式,可以直接创建一个列表。但是,有些时候,受到内存的限制等实际情况,列表生成式无法满足。比如,一个长度为1000万的列表,普通内存根本就不够,又或者实际处理的过程中,我们只需要访问前面几个元素,那后面的的绝大部分的空间都浪费了。

    思路:如果能做到一开始并不是创建完整的list,而是通过定义一种规则的方式,在循环的过程中不断的推算后续的元素,达到使用到哪个元素才生成哪个元素的效果?在Python中,这种机制称为生成器:generator。

    创建generator,方法一:

    >>> m = (x for x in range(10))
    >>> m
    <generator object <genexpr> at 0x0376BF00>

    观察可知,和列表生成式相比,区别仅仅在于将最外层的[]换成()。请注意,m并不是一个list,而是一个generator。如何打印generator中的每一个元素呢?笨重方法(该方法基本用不到):

    >>> next(m)
    0
    >>> next(m)
    1
    >>> l = ['hah','hehe']
    >>> next(m)
    2

    中间有个小插曲,随便做了一个操作,紧接着我们又调用next函数,发现结果还是按照算法计算出下一个值。(当生成器没有更多的元素的时候,会抛出StopIteration错误)

    方便的取元素方法:因为generator是可迭代对象(从StopIteration错误类型,我们也可以猜测出来),我们可以使用for循环实现取数:

    >>> n = (a+b for a in 'abc' for b in 'xyz')
    >>> for i in n:
    ...     print(i)
    ...
    ax
    ay
    az
    bx
    by
    bz
    cx
    cy
    cz

    方法二:

    如果上述中的推算算法比较复杂,使用方法一无法实现的时候,可以使用函数来实现。比如著名的斐波拉契数列(1,1,2,3,5,8,13,21……除了第一个和第二个数外,任意一个数都是由其前两个数相加的和)。斐波拉契数列使用列表生成式写不出来,可以使用函数把它打印出来:

    >>> def fib(max):
    ...     n,a,b = 0,0,1
    ...     while n < max:
    ...             print (b)
    ...             a,b = b,a+b#相当于将一个tuple(b,a+b)赋值给a,b
    ...             n = n + 1
    ...     return
    ...
    >>> fib (6)
    1
    1
    2
    3
    5
    8

    其实,上述fib()和generator非常相近了。只需要把print(b)变成yield b 就可以了:

    >>> def fib(max):
    ...     n,a,b = 0,0,1
    ...     while n < max:
    ...             yield b
    ...             a,b = b,a+b
    ...             n = n+ 1
    ...     return
    ...
    >>> fib(6)
    <generator object fib at 0x037DA120>

    这就是定义generator的第二种方法。如果一个函数中包含yield关键字,那么这个函数就不再是普通函数,而是一个generator。两者的执行流程可以这么区别:普通函数是顺序执行,遇到return或者最后一行代码函数就会返回。而generator,在每次调用next()的时候执行,遇到yield语句返回。再次执行的时候,从上次返回的yield语句处继续执行。

    使用for循环来迭代:

    >>> m = fib(5)
    >>> for i in m :
    ...     print(i)
    ...
    1
    1
    2
    3
    5

    那么如何获取一个generator中的return的值呢?这时必须捕获StopIteration错误,返回值就包含在StopIteration的value中:

    >>> def fib(max):
    ...     n ,a,b  = 0,0,1
    ...     while n < max:
    ...             yield b
    ...             a,b = b,a+b
    ...             n = n+1
    ...     return 'Over'
    ...
    >>> m = fib(6)
    >>> while True:
    ...     try:
    ...             x = next(m)
    ...             print(x)
    ...     except StopIteration as e:
    ...             print(e.value)
    ...             break
    ...
    1
    1
    2
    3
    5
    8
    Over

    练习:

    杨辉三角:

              1      n=0
             /       
            1   1     n=1
           /  /       
          1   2   1    n=2
         /  /  /       
        1   3   3   1   n=3
       /  /  /  /     
      1   4   6   4   1  n=4
     /  /  /  /  / 
    1   5   10  10  5   1 n=5
    杨辉三角,把二项式系数图形化,把组合数内在的一些代数性质直观的从图形中表现出来,是一种离散型的数与形的优美结合。
    有如下规律:
    1,每行端点和结尾的数为1;
    2、每行数左右对称,由1开始逐渐变大;
    3、第n行有n项;
    4、第n行数字之和为2的n-1次方;
    5、第n行的m个数可表示为C(n-1,m-1),即为从n-1个不同元素中取m-1个元素的组合数;
    6、第n行的第m个数和n-m+1个数相等,为组合数性质之一;
    7、每个数字等于上一行的左右两个数字之和;(利用此性质可写出整个杨辉三角)
    8、(a+b)

    n

    的展开式中的各项系数依次对应杨辉三角的第(n+1)行中的每一项
    如果把杨辉三角的每一行看做一个list,试写一个generator,不断输出下一行的list:
    >>> def triangle():
    ...     l=[1]
    ...     while True:
    ...             yield l
    ...             l.append(0)
    ...             l= [l[i-1]+l[i] for i in range(len(l))]
    ...

    验证一下:

    >>> x = triangle()
    >>> next(x)
    [1]
    >>> next(x)
    [1, 1]
    >>> next(x)
    [1, 2, 1]
    >>> next(x)
    [1, 3, 3, 1]
    >>> next(x)
    [1, 4, 6, 4, 1]
    >>> next(x)
    [1, 5, 10, 10, 5, 1]
    >>> next(x)
    [1, 6, 15, 20, 15, 6, 1]
    >>> next(x)
    [1, 7, 21, 35, 35, 21, 7, 1]
    >>> next(x)
    [1, 8, 28, 56, 70, 56, 28, 8, 1]
    >>> next(x)
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
    >>> next(x)
    [1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
    >>> next(x)
    [1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
    >>> next(x)
    [1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]

    收工!

  • 相关阅读:
    23岁的这一年
    迁移ORACLE数据库文件到ASM
    无归档情况下使用BBED处理ORA-01113错误
    Oracle RMAN-06023 和ORA-19693错误
    手工创建Oracle数据库
    使用BBED理解和修改Oracle数据块
    Oracle 11g 重建EM需要删除的对象
    各大主流编程语言-常用爬虫框架以及优劣分析
    pyspider入门
    Centos7上安装docker及使用scrapy-splash
  • 原文地址:https://www.cnblogs.com/hiwuchong/p/8060266.html
Copyright © 2020-2023  润新知