python生成器与迭代器初识

通过列表生成式,可以直接创建一个列表,例如:

>>> L = [x*x for x in range(11)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但是,受到内存限制,导致列表的容量是有限的。从而引入生成器(generator)这一概念,这样就不必创建完整的list,可以边循环变计算,从而节省了大量的储存空间。

创建生成器有两种方法,第一种方法是把一个列表生成式中的[]变为(),例如:

>>> g = (x * x for x in range(11))
>>> g
<generator object <genexpr> at 0x024CE4E0>

第二种方法是在函数定义中包含yield关键字,那么这个函数就不再是普通函数,而是一个生成器(generator),例如:

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

如何取得生成器(generator)的返回值?

可以使用next()函数获得generator的下一个返回值,例如:

>>> g = (x * x for x in range(11))
>>> g
<generator object <genexpr> at 0x024CE6C0>
>>> 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)
100
>>> next(g)
Traceback (most recent call last):
  File "<pyshell#61>", line 1, in <module>
    next(g)
StopIteration

generator保存的是算法,每次调用next(g),就计算g的下一个元素的值,直到计算到最后一个元素,当没有更多的元素时,将显示StopIteration错误。

不过使用next()函数过于麻烦,可以使用for循环可显示生成器的返回值,例如:

>>> g = (x + x for x in range(11))
>>> g
<generator object <genexpr> at 0x024CE600>
>>> for i in g:
	print(i)

	
0
2
4
6
8
10
12
14
16
18
20

获取含yield的生成器的返回值,同样也可以使用next()函数以及for循环

>>> def fib(max):
	for i in range(max):
		yield(i)

		
>>> fib(6)
<generator object fib at 0x024CE600>
>>> next(fib(6))
0
>>> next(fib(6))
0
>>> g = fib(6)
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
5
>>> next(g)
Traceback (most recent call last):
  File "<pyshell#90>", line 1, in <module>
    next(g)
StopIteration
>>> for a in fib(8):
	print(a)

	
0
1
2
3
4
5
6
7

使用next(fib(6))两次都是从头开始执行,由于每次使用next()函数调用都是全新的生成器,当将fib(6)赋予g时,每次使用next(g)都可以获取下一个返回值,而不是从头开始。


迭代器

可以使用for循环的数据类型包括:

1. 集合数据类型,如list、tuple、dict、set、str等;

2. 生成器(generator)

这些可以使用for循环的对象统称为可迭代对象(Iterable)

而可以被next()函数调用并不断返回下一个值的对象成为迭代器(Iterator),生成器都是迭代器(Iterator)对象,但list、tuple、dict、str虽然是Iterable,但不是Iterator。

可使用iter()函数将list、tuple、dict、str变为Iterator。

可使用isinstance()函数来判断一个对象的类型,如下:

>>> from collections import Iterable, Iterator
>>> isinstance([], Iterable)
True
>>> isinstance([], Iterator)
False
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance((x for x in range(11)), Iterator)
True
>>> isinstance(iter([]), Iterator)
True
>>> isinstance({}, Iterator)
False
>>> isinstance((1,), Iterable)
True
>>> isinstance((1, ), Iterator)
False
>>> type((1, ))
<class 'tuple'>
>>> isinstance(iter((1, )), Iterator)
True

你可能会问,为什么list、tuple、dict、str等数据类型不是Iterator

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时显示StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

猜你喜欢

转载自blog.csdn.net/qq_42020470/article/details/80977103