1.迭代器协议
对象必须提供一个next(),执行该方法要么返回迭代中的一个,要么就引起一个Stopiteration异常,以终止迭代
2.可迭代对象
实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)
3.协议是一种约定,可迭代对象实现了迭代器协议,python内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象
4.for循环的本质
for循环的本质:循环所有的对象,全都是使用迭代器协议
for可以遍历字符串、列表、元祖、字典、文件对象,但是这些数据类型本身不是可迭代对象,for循环执行时,实际上是先调用他们内部的__iter__()方法,将其转换为可迭代对象,在通过next()方法获取下一个值,直到for循环捕捉到StopIterration终止循环。
#例如: a='test' s=a.__iter__() #字符串转化为可迭代对象 print(s) #--> <str_iterator object at 0x02876B30> print(s.__next__()) #--> t 字符串转化为可迭代对象后,才会具有next()方法的能力 print(s.__next__()) #--> e print(s.__next__()) #--> s print(s.__next__()) #--> t
li=[1,2,3,4,5] l=li.__iter__() #遵循迭代器协议,生成可迭代对象 print(l) #--> <list_iterator object at 0x03006B30> print(l.__next__()) #--> 1 print(l.__next__()) #--> 2 print(l.__next__()) #--> 3 print(l.__next__()) #--> 4 print(l.__next__()) #--> 5
5.生成器
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用其内部的__iter__()方法),所以生成器就是可迭代对象
生成器分类:
(1)生成器函数
常规函数定义,但是使用yield语句而不是return语句返回结果。yeild语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便于下次重它离开的地方继续执行
def test(): yield 1 yield 2 yield 3 yield 4 g=test() print(g.__next__()) #-->1 print(g.__next__()) #-->2 print(g.__next__()) #-->3 print(g.__next__()) #-->4
(2)生成器表达式
类似于列表推导,但是,生产器返回按需产生结果的一个对象,而不是构建一个结果列表
for i in range(10): print(i) l=[i for i in range(10)] #列表推导式(列表解析:内存占用大,机器容易卡死) print(l)
#生成器表达式 几乎不占内存 res=(i for i in range(10) if i<3) print(res) #<generator object <genexpr> at 0x03921300> 这里的res就是一个生成器 print(next(res)) #-->0 print(res.__next__()) #-->1 print(res.__next__()) #-->2 print(res.__next__()) #-->StopIteration 抛异常
(3)生成器只能遍历一次
def test(): for i in range(4): yield i t=test() t1=(i for i in t) t2=(i for i in t1) print(list(t1)) #list(t1)将t1生成器生成的值已经遍历完成,输出:[0,1,2,3] print(list(t2)) #t2=(i for i in t1),t1已经遍历完成,t1为空,t2再去遍历t1时,只能得到空值
生成器的优点:
Python使用生成器对延迟操作提供了支持,所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处
生成器小节:
1.是可迭代对象
2.实现了延迟计算省内存
3.生成器本质和其他的数据类型一样,都是实现了迭代器协议,只不过生成器附加了一个延迟计算省内存的好处,其余的可迭代对象没有这点好处