迭代器Iterator
🖊能被next()函数调用,并不断返回下一个值的对象称为迭代器(Iterator 迭代器对象),迭代器是访问集合元素的一种方式,是python中最具特色的功能之一。
🖊迭代器可以记住访问遍历的位置,从集合的第一个元素开始访问,直到集合中的所有元素被访问完毕。迭代器只能从前往后一个一个的遍历,不能后退。
🖊迭代器的优势在于支持自身遍历,同时,它的特点是单向非循环的,一旦完成遍历,再次调用就会报错。
比如:普通可迭代对象就像是子弹匣,它遍历就是取出子弹,在完成操作后又装回去,所以可以反复遍历(即多次调用for循环,返回相同结果);而迭代器就像是装载了子弹匣而不可拆卸的枪,进行它遍历或自遍历都是发射子弹,这是消耗性的遍历,是无法复用的(即遍历会有尽头)。
1.相关函数
iter()
功能: 把可迭代的对象转为一个迭代器对象
参数: 可迭代的对象 (str,list,tuple,dict)
返回值:迭代器对象
next()
功能:调用迭代器,并返回迭代器中的下一个数据
2.迭代器取值
特点: 取出一个少一个,直到都取完,最后再取就会报错
迭代器取值方案:
1. next() 调用一次获取一次,直到数据被取完
2. list() 使用list函数直接取出迭代器中的所有数据
3. for 使用for循环遍历迭代器的数据
举例:
# 定义一个列表,是一个可迭代的对象
f4 = ['赵四','刘能','小沈阳','海参炒面']
res = iter(f4) # 把可迭代对象转为迭代器使用 iter()
print(res,type(res)) #<list_iterator object at 0x109063810> <class 'list_iterator'>
# 1.使用next()函数去调用迭代器对象
print(next(res)) #赵四
print(next(res)) #刘能
#2.使用list取值
r = list(res)
print(r) #直接运行2,返回['赵四', '刘能', '小沈阳', '海参炒面']
#运行完1再运行2,返回['小沈阳', '海参炒面']
#3.使用for循环
for i in res:
print(i) #直接运行3,返回:
'''
赵四
刘能
小沈阳
海参炒面
'''
# print(next(res)) #报错:StopIteration, 即超出可迭代的范围
3.检测迭代器和可迭代对象的方法
迭代器一定是一个可迭代的对象,可迭代对象不一定是迭代器
迭代是一种遍历元素的方式,按照实现方式划分,有外部迭代与内部迭代两种,支持外部迭代(它遍历)的对象就是可迭代对象,而同时还支持内部迭代(自遍历)的对象就是迭代器;按照消费方式划分,可分为复用型迭代与一次性迭代,普通可迭代对象是复用型的,而迭代器是一次性的。
from collections.abc import Iterator,Iterable
varstr = '123456' #可迭代对象
res = iter(varstr) #可迭代对象+迭代器
r1 = isinstance(varstr,Iterable) # True 可迭代对象
r2 = isinstance(varstr,Iterator) # False 不是一个迭代器
r3 = isinstance(res,Iterable) # True 可迭代对象
r4 = isinstance(res,Iterator) # True 是一个迭代器
print(r1,r2)
print(r3,r4)
type() 函数返回当前数据的类型,
isinstance() 检测一个数据是不是一个指定的类型
4.迭代器切片
itertools模块的 islice()方法将迭代器与切片完美结合
为了更直观地感受迭代器内部的执行过程,我们自定义一个迭代器,以斐波那契数列为例:
from itertools import islice
class Fib:
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value
f = Fib()
print(list(islice(f, 0, 10)))
#返回[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Fib既是一个可迭代对象(因为它实现了 __iter__方法),又是一个迭代器(因为实现了 __next__方法)。
实例变量prev和curr用户维护迭代器内部的状态。
每次调用 next()方法的时候做两件事:
- 1.为下一次调用 next()方法修改状态
- 2.为当前这次调用生成返回结果
迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。
itertools 模块的切片方法的实现逻辑参看下方官网提供的源码:
def islice(iterable, *args):
# islice('ABCDEFG', 2) --> A B
# islice('ABCDEFG', 2, 4) --> C D
# islice('ABCDEFG', 2, None) --> C D E F G
# islice('ABCDEFG', 0, None, 2) --> A C E G
s = slice(*args)
# 索引区间是[0,sys.maxsize],默认步长是1
start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1
it = iter(range(start, stop, step))
try:
nexti = next(it)
except StopIteration:
# Consume *iterable* up to the *start* position.
for i, element in zip(range(start), iterable):
pass
return
try:
for i, element in enumerate(iterable):
if i == nexti:
yield element
nexti = next(it)
except StopIteration:
# Consume to *stop*.
for i, element in zip(range(i + 1, stop), iterable):
pass
islice() 方法的索引方向是受限的,但它也提供了一种可能性:即允许你对一个无穷的(在系统支持范围内)迭代器进行切片的能力。这是迭代器切片最具想象力的用途场景。
5.小结
迭代器是一种特殊的可迭代对象,可用于它遍历与自遍历,但遍历过程是损耗型的,不具备循环复用性,因此,迭代器本身不支持切片操作;通过借助 itertools 模块,我们能实现迭代器切片,将两者的优势相结合,其主要用途在于截取大型迭代器(如无限数列、超大文件等等)的片段,实现精准的处理,从而大大地提升性能与效率。