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

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

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

建立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错误)spa

方便的取元素方法:由于generator是可迭代对象(从StopIteration错误类型,咱们也能够猜想出来),咱们可使用for循环实现取数:code

>>> 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

方法二:orm

若是上述中的推算算法比较复杂,使用方法一没法实现的时候,可使用函数来实现。好比著名的斐波拉契数列(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 就能够了:blog

>>> 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,每行端点和结尾的数为1;
二、每行数左右对称,由1开始逐渐变大;
三、第n行有n项;
四、第n行数字之和为2的n-1次方;
五、第n行的m个数可表示为C(n-1,m-1),即为从n-1个不一样元素中取m-1个元素的组合数;
六、第n行的第m个数和n-m+1个数相等,为组合数性质之一;
七、每一个数字等于上一行的左右两个数字之和;(利用此性质可写出整个杨辉三角)
八、(a+b)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

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]

收工!

相关文章
相关标签/搜索