从零开始学Python-Day22-生成器

Python零基础 木人张 3年前 (2020-03-26) 748次浏览 0个评论 扫描二维码
文章目录[隐藏]

生成器

上一节列表生成式可以用来生成一个完整list,但是如果需要的list容量很大呢?如果需要一个100万个元素的列表,难道要生成这样一个list么,那不是很占内存么?更何况我们可能并不需要这个列表中的所有元素。
是不是没有必要完整生成一个list,而是把规律和算法写入,让其自动推算所有元素呢?Python提供了这样一个工具:生成器generator,通过一边循环一边计算:

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

>>> L=[x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g=(x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x112fd8f20>
>>> 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 call last):
  File "<pyshell#38>", line 1, in <module>
    next(g)
StopIteration
>>> 

L和g的区别仅仅在于创建时一个用的[]一个用的(),L是一个list,g是一个生成器,我们直接输入L就输出了整个list的元素,上面的例子中,我们使用next函数把生成器中每个元素一个个输出出来,当超出生成器的范围时,就直接报错StopIteration(停止迭代)。
生成器保存的是算法,像上面用next一个一行的输出太繁琐,我们可以使用for循环的方式迭代输出:

>>> f=(x*x for x in range(10))
>>> for n in f:
	print(n)

0
1
4
9
16
25
36
49
64
81

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

>>> def fib(max):
	n,a,b = 0,0,1
	while n<max:
		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就可以:

>>> def fib(max):
	n,a,b=0,0,1
	while n<max:
		yield b
		a,b=b,a+b
		n=n+1
	return 'done'

>>> f=fib(6)
>>> f
<generator object fib at 0x11309f120>
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
Traceback (most recent call last):
  File "<pyshell#86>", line 1, in <module>
    next(f)
StopIteration: done

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

>>> def odd():
	print('step 1')
	yield 1
	print('step 2')
	yield 3
	print('step 3')
	yield 5

>>> o=odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<pyshell#99>", line 1, in <module>
    next(o)
StopIteration

上面的例子可以看到,调用生成器首先要有一个对象o,用next挨个输出时,遇到yield就中断,再次next就接着上次的位置继续,直到最后一次没有可执行的位置了,就直接报错了。
这里next只是为了展示运算的过程,在实际使用中我们依然使用for循环来迭代,接着上面的fib生成器:

>>> for n in fib(7):
	print(n)

1
1
2
3
5
8
13
>>> 

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

>>> g=fib(7)
>>> while True:
	try:
		x=next(g)
		print('g:',x)
	except StopIteration as e:
		print(e.value)
		break

g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
g: 13
done

木人张,版权所有丨如未注明 , 均为原创,禁止转载。
喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址