迭代器
迭代器(Iterator)概述
迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。
迭代器不能回退,只能往前进行迭代。这并不是什么很大的缺点,因为人们几乎不需要在迭代途中进行回退操作。
迭代器也不是线程安全的,在多线程环境中对可变集合使用迭代器是一个危险的操作。但如果小心谨慎,或者干脆贯彻函数式思想坚持使用不可变的集合,那这也不是什么大问题。
对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值,这是后话)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。
迭代器的另一个优点就是它不要求你事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。这个特点被称为延迟计算或惰性求值(Lazy evaluation)。
迭代器更大的功劳是提供了一个统一的访问集合的接口。只要是实现了__iter__()方法的对象,就可以使用迭代器进行访问。
使用迭代器
使用内建的工厂函数iter(iterable)可以获取迭代器对象,使用迭代器next()方法可以访问下一个元素:
lst=range(2)
lt=iter(lst)
print(lt)
#打印的是一个地址<range_iterator object at 0x053C0F50>
it.next()
#打印出来的是 0
from collections import Iterator 判断是否是可迭代对象,即是否可以使用next()方法迭代的
可迭代对象
什么是可迭代对象? list,dict,set,str,generator(生成器),iterator(迭代器),全部都是可迭代对象(英语叫Iterable)。
可迭代对象都可以使用for循环遍历。
制作一个简单的迭代器
除了使用iter()
函数将内置的序列对象转换成相应的迭代器,我们可以自己实现迭代器协议创建迭代器对象,要实现迭代器协议也就是要在类中实现__iter__()
和__next__()
方法。
class Container:
def __init__(self,start=0,end=0):
self.start=start
self.end=end
def __iter__(self):
print("我制作这个迭代器!")
return self
def __next__(self):
print("通过 __next__方法进行迭代")
if self.start<self.end:
i=self.start
self.start+=1
return i
else:
raise StopIteration()
c=Container(0,5)
for i in c:
print(i)
迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。
注意:迭代器只能遍历一次 不能进行二次遍历
生成器(generator)
生成器简介
首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。另外,对于生成器的特殊语法支持使得编写一个生成器比自定义一个常规的迭代器要简单不少,所以生成器也是最常用到的特性之一。
如何获取一个生成器?首先来看一小段代码:
def get_0_1_2():
yield 0
yield 1
yield 2
print(get_0_1_2)
#打印出来的是<function get_0_1_2 at 0x055BB618>
我们定义了一个函数get_0_1_2,并且可以查看到这确实是函数类型。但与一般的函数不同的是,get_0_1_2的函数体内使用了关键字yield,这使得get_0_1_2成为了一个生成器函数。
第一种方式:
将列表的[]改为(),里面可以写推导
生成器只要调用next()方法即可得到下一个值,到最后一个后异常(StopIteration)退出
用表达式做生成器
例
test=(x for x in rang(10))
next(test)
#或
test.__next__()
第二种方式:
通过裴波那切序列讲解:将print换成yield,每次生成一个值
但是调用方法时没有结果
在方法内使用yield做生成器,这样调用此方法时,会得出一个结果:generator object...
然后也是可以调用next()
每到yield时,就会停止,返回当前这个值,可以一次一次next()调用,看看运行过程
def fbnq(m):
n,a,b=0,0,1
while n<m:
yield(b)
a,b=b,b+a
n+=1
a=fbnq(10)
print(a.__next__())#打印的是1
print(a.__next__())#打印的是1
print(a.__next__())#打印的是2
#以此类推
生成器的特点
1.语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值
2.自动实现迭代器协议:对于生成器,Python会自动实现迭代器协议,以便应用到迭代背景中(如for循环,sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常
3.状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行4.生成器可以被循环迭代,直到循环结束