通过上节的学习,我们知道使用列表生成式,可以直接创建一个列表。但是,有些时候,受到内存的限制等实际情况,列表生成式无法满足。比如,一个长度为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]
收工!