1.迭代器(Iterator)
可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
你可能会问,为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
迭代器在类中的应用
class fibs:
def __init__(self, a, b):
self.a = a
self.b = b
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
return self.b
f = fibs(0, 1)
建立了一个斐波那契数列的计算类,该类可以被迭代,需要获取前两个数值,调用next()便可以输出下一个斐波那契数。
>>>f.__next__()
1
>>>f.__next__()
2
>>>f.__next__()
3
>>>f.__next__()
5
稍作修改,可以输出某个范围内的斐波那契数列:
class fibs:
def __init__(self, a, b, n):
self.a = a
self.b = b
self.n = n
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.b < self.n:
return self.b
else:
raise StopIteration
f = fibs(0, 1, 100)
for i in f:
print(i)
1
2
3
5
8
13
21
34
55
89
2.生成器(generator)
yield是生成器的灵魂,生成器是一种特殊的迭代器,是迭代器的一种实现,像使用迭代器需要定义一个类,来实现完整的功能,在生成器中,只需要使用yield
,便可以实现函数的暂停与启用,就类似于单片机中的中断,使用起来更加方便。
def Gen():
yield print('生成器被调用1次')
yield print('生成器被调用2次')
mygen = Gen()
>>>next(mygen)
生成器被调用1次
>>>next(mygen)
生成器被调用2次
>>>next(mygen)
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration
当一个生成器被执行next()
时,会执行到yield
的位置暂停,并返回yield
后的内容,直到下一次执行next()
,会从上一次暂停的位置继续执行,当没有元素可以执行时,会抛出一个StopIteration
的异常,当然这和迭代器一样,可以使用for in
来迭代
>>>for i in mygen:
pass
生成器被调用1次
生成器被调用2次
同样,我们用生成器来实现一下斐波那契:
def fibs(a, b, n):
while True:
a, b = b, a+b
if b < n:
yield print(b, end=' ') #end=' '以空格为间隔输出b
else:
raise StopIteration
for i in fibs(0,1,100):
pass
1 2 3 5 8 13 21 34 55 89
是不是感觉比迭代器来的方便了呢!