Python-生成器

生成器

上一节列表生成式可以用来生成一个完整list,但是如果需要的list容量很大呢?如果需要一个100万个元素的列表,难道要生成这样一个list么,那不是很占内存么?更何况我们可能并不需要这个列表中的所有元素。

是不是没有必要完整生成一个list,而是把规律和算法写入,让其自动推算所有元素呢?Python提供了这样一个工具:生成器generator,通过一边循环一边计算:

一个简单的生成器创建方法就是把列表生成式的[]变成:

>>>L=[x*xforxinrange(10)]

>>>L

[0,1,4,9,16,25,36,49,64,81]

>>>g=(x*xforxinrange(10))

>>>g

<generatorobjectat0x112fd8f20 >

>>>next(g)

0

>>>next(g)

1

>>>next(g)

4

>>>next(g)

9

>>>next(g)

16

>>>next(g)

25

>>>next(g)

36

>>>next(g)

49

>>>next(g)

64

>>>next(g)

81

>>>next(g)

Traceback(most recent calllast):

File"<pyshell#38>",line1,in

next(g)

StopIteration

>>>

L和g的区别仅仅在于创建时一个用的[]一个用的,L是一个list,g是一个生成器,我们直接输入L就输出了整个list的元素,上面的例子中,我们使用next函数把生成器中每个元素一个个输出出来,当超出生成器的范围时,就直接报错StopIteration(停止迭代)。

生成器保存的是算法,像上面用next一个一行的输出太繁琐,我们可以使用for循环的方式迭代输出:

>>>f=(x*xforxinrange(10))

>>>forninf:

print(n)

0

1

4

9

16

25

36

49

64

81

v对于复杂的生成器,for循环无法表达时,可以通过函数实现,例如斐波拉契数列(Fibonacci),头两个数是1和1,后面每个数都是前两个数之和,也就是:

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

>>>deffib(max):

n,a,b=0,0,1

whilen<max:< p="">

print(b)

a,b=b,a+b

n=n+1

return'done'

>>>fib(7)

1

1

2

3

5

8

13

'done'

函数fib其实就是定义了裴波拉切数列的推算方式,这和生成器是类似的,那么如何把这个函数变成一个生成器呢?只需要把print(b)变成yield b就可以:

>>>deffib(max):

n,a,b=0,0,1

whilen<max:< p="">

yieldb

a,b=b,a+b

n=n+1

return'done'

>>>f=fib(6)

>>>f

 

>>>next(f)

1

>>>next(f)

1

>>>next(f)

2

>>>next(f)

3

>>>next(f)

5

>>>next(f)

8

>>>next(f)

Traceback(most recent calllast):

File"<pyshell#86>",line1,in

next(f)

StopIteration:done

这样fib就变成一个生成器了,函数执行是顺序执行,遇到return或者执行到底了就返回,而在生成器中,每次next都是遇到yield就返回,下次next从上次yield的位置继续执行。我们定义一个生成器依次输出1、3、5:

>>>defodd:

print('step 1')

yield1

print('step 2')

yield3

print('step 3')

yield5

>>>o=odd

>>>next(o)

step1

1

>>>next(o)

step2

3

>>>next(o)

step3

5

>>>next(o)

Traceback(most recent calllast):

File"<pyshell#99>",line1,in

next(o)

StopIteration

上面的例子可以看到,调用生成器首先要有一个对象o,用next挨个输出时,遇到yield就中断,再次next就接着上次的位置继续,直到最后一次没有可执行的位置了,就直接报错了。

这里next只是为了展示运算的过程,在实际使用中我们依然使用for循环来迭代,接着上面的fib生成器:

>>>forninfib(7):

print(n)

1

1

2

3

5

8

13

>>>

我们看到用for迭代生成器fib时,并没有输出最后返回值’done’,要想拿到返回值就需要捕捉最后一次迭代执行时,终止迭代的报错信息StopIteration,’done’这个返回值就包含在这里:

>>>g=fib(7)

>>>whileTrue:

try:

x=next(g)

print('g:',x)

exceptStopIterationase:

print(e.value)

break

g:1

g:1

g:2

g:3

g:5

g:8

g:13

done

上一篇:计算斐波那契数列的性能对比:Python,Java,Go


下一篇:用递归函数求n的阶乘及斐波那契数列中的第n个数的值