迭代器
能够用for循环的数据类型有以下几种: 一种是集合数据类型,如
str,list,tuple,dict,set
等;
一种是generator
,包括生成器和带yield
的generator function
。
这些直接可以用for
循环的对象统称为可迭代的对象iterable
,可以用isinstance()
判断 一个对象是否是一个iterable
对象:
>>> from collections import Iterable
>>> isinstance((),Iterable)
True
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('abc',Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
>>> isinstance(set,Iterable)
False
>>> isinstance(({},),Iterable)
True
>>> isinstance(100,Iterable)
False
迭代器(
iterator
)的实质是实现next()
方法的对象,也就是说可以被next()
函数调用并不断返回下一个值的对象,迭代其中重点关注两个方法:
__iter__()
方法 :返回迭代器本身,python内建函数iter()
本质上就是不断调用 该方法;
__next__()
方法:当next()
方法被调用的时候,迭代器会返回它的下一个值,如果next()
方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration
异常,python 内建函数next()调用该方法。
>>> it = ([1,2,3,4,5])
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
>>> it = iter([1,2,3,4,5])
>>> next(it)
1
>>> it.__next__()
2
>>> next(it)
3
>>> next(it)
4
>>> next(it)
5
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
总结:
**1)**可迭代对象包含迭代器。
**2)**如果一个对象拥有__iter__()
方法,其是可迭代对象;如果一个对象拥有next()
方法,其是迭代器。
**3)**定义可迭代对象,必须实现__iter__()
方法;定义迭代器,必须实现__iter__()
和next()
方法。
4)list、dict、str
虽然是Iterable
(可迭代对象),却不是Iterator
(迭代器),不过可以通过iter()
函数获得一个Iterator
对象,生成器都是Iterator
对象。
**5)**凡是可作用于for
循环的对象都是Iterable
类型。
**6)**凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列。
- 那么为什么
list、dict、str
等数据类型不是Iterator
(迭代器)?这是因为Python中的
list、dict、str
类型自身实现了一个__iter__()
方法使其变成iterable
。
Iterator
对象表示的是一个数据流,Iterator
对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
生成器
生成器是一个特殊的迭代器,自动实现了迭代器协议(即
__iter__()
和__next__()
/next()
方法),必须要再手动实现这两种方法。
创建生成器的方法有很多种:
1.只要把一个列表生成式的[]改成(),就创建了一个generator
:
>>> L = [2*x for x in range(10)]
>>> L
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> g = (2*x for x in range(10))
>>> g
<generator object <genexpr> at 0x005AA670>
此时可以利用
next()
方法取出每一个值,不过一般都是用for
循环来取出生成器的值。
2.生成是包含yield
关键字的函数,看fib()
函数:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'just do it'
要把
fib()
函数变成generator
,只需要把print(b)
改为yield
就可以了,如果一个函数中包含了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 'just do it'
关键字
yield
是一个语法糖,内部实现支持了迭代器协议,同时yield
内部是一个状态机,维护着挂起和继续的状态。
- 生成器是怎么调用执行的呢?只需要了解下面几条规则即可:
1). 当生成器被调用的时候,函数体的代码不会被执行,而是会返回一个迭代器,其实,生成器函数返回生成器的迭代器。
“生成器的迭代器”这个术语通常被称作”生成器”。要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是next()
。如同迭代器一样,我们可以使用next()
函数来获取下一个值。需要明白的是,这一切都是在yield
内部实现的。
2). 当next()
方法第一次被调用的时候,生成器函数才开始执行,执行到yield
语句处停止,next()
方法的返回值就是yield
语句处的参数(yielded value
),当继续调用next()
方法的时候,函数将接着上一次停止的yield
语句处继续执行,并到下一个yield
处停止;如果后面没有yield
就抛出StopIteration
异常。3).每调用一次生成器的
next()
方法,就会执行生成器中的代码,直到遇到一个yield
或者return
语句。yield
语句意味着应该生成一个值(在上面已经解释清楚)。return
意味着生成器要停止执行,不再产生任何东西。4). 生成器的编写方法和函数定义类似,只是在
return
的地方改为yield
。生成器中可以有多个yield
。当生成器遇到一个yield
时,会暂停运行生成器,返回yield
后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield
。生成器自身又构成一个循环器,每次循环使用一个yield
返回的值,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。