python中的迭代器、可迭代对象和生成器
1. 前言
迭代是一种遍历容器类型对象(例如字符串、列表、字典等等)的方式。例如,我们说迭代一个字符串“abc”,指的就是从左往右依次地、逐个地取出它的全部字符的过程(可以将迭代理解为遍历)。Python的迭代机制依赖于两个特殊方法:__iter__
和__next__
。__iter__
方法返回一个迭代器对象,而__next__
方法则负责返回迭代器的下一个值。当没有更多的值可返回时,__next__
会抛出StopIteration异常。(这一段看不懂没关系!!!,继续往后看就明白了)
我觉得可迭代对象(iterable)和迭代器(iterator)的概念有点抽象,容易把我搞懵,那怎么办呢。其实我们可以使用python的内置函数isinstance(obj, classinfo)
来查询某个对象(obj)是否为指定的类型(classinfo),该函数的返回值为True或False。也就是说,我们可以使用内置函数isinstance(obj, classinfo)
来判断该对象是否为可迭代对象或迭代器对象。代码示例如下(代码1):
# 代码1
from collections.abc import Iterable, Iterator
# 需要导入Iterable, Iterator
obj_list = [1,2,3,4]
obj_tuple = (1,2,3)
print(isinstance(obj_list, Iterable)) # 输出:True
print(isinstance(obj_list, Iterator)) # 输出:False
print(isinstance(obj_tuple, Iterable)) # 输出:True
print(isinstance(obj_tuple, Iterator)) # 输出:False
由上面代码可知:列表、元组都是可迭代对象(iterable),都不是迭代器对象(iterator)。下面介绍两个和迭代器相关的内置函数:iter()
和next()
。
(1)iter()
:该函数可以将可迭代对象(iterable)转换为迭代器(iterator),即iter()
函数用于创建一个迭代器对象。iter()
函数实际映射到类中的__iter__
方法,即执行iter()
函数会调用类中的__iter__
方法。
(2)next()
:该函数可以返回迭代器(iterator)中的下一个元素,如果没有更多的元素,则引发StopIteration 异常。如果我们使用内置函数next()
对迭代器进行遍历,在这个过程中,是在调用迭代器的__next__
方法。即:next()
函数内部调用迭代器的__next__
方法。
from collections.abc import Iterable, Iteratormy_list = [1,2,3]
iter_list = iter(my_list) # 将列表转换成迭代器对象
print(isinstance(iter_list, Iterable)) # 输出:True
print(isinstance(iter_list, Iterator)) # 输出:True
print(next(iter_list)) # 输出:1
print(next(iter_list)) # 输出:2
print(next(iter_list)) # 输出:3
print(next(iter_list)) # 输出:StopIteration
2. for循环遍历的原理
大家对for循环不陌生吧,其实在for循环中就使用了iter()
和next()
这两个内置函数。Python的for循环本质上就是通过不断调用迭代器的next()
函数实现的,直到捕获 StopIteration 异常为止。在进行for循环遍历过程中就做两件事:(1)第一件:使用iter()
函数将可迭代对象(iterable)转为迭代器对象(iterator);(2)第二件:反复对迭代器对象(iterator)使用next()
函数,直到迭代器中的元素被获取完。最后捕获StopIteration异常,退出循环。对了,对于这个for循环语句for ... in ...
,关键字in
后面需要跟可迭代对象(iterable)。顺便再说一句:迭代器(iterator)一定是可迭代对象(iterable),即迭代器也是可迭代的。
my_list = [1,2,3]
for i in my_list:print(i)# python 内部是这样转化的:
my_list = [1,2,3]
for i in iter(my_list ): # 将列表(可迭代对象)转化迭代器(iterator)print(i)# for循环会自动处理这个StopIteration异常,以便结束for循环。
我们也可以通过next()
函数来遍历列表中的元素,代码示例如下(代码2)。由下面代码可知:next()
函数依次取出迭代器中的元素,直到抛出StopIteration异常(如果不将列表(可迭代对象)通过iter()
函数转换为迭代器对象,是没法使用next()
函数来取出列表中的元素)。
# 代码2
my_list = [1,2,3]
iter_list = iter(my_list) # 将列表转换成迭代器对象
print(next(iter_list)) # 输出:1
print(next(iter_list)) # 输出:2
print(next(iter_list)) # 输出:3
print(next(iter_list)) # 输出:StopIteration
3. 可迭代对象(iterable)
如果一个对象实现了__iter__
方法,那么这个对象就是可迭代对象。常见的可迭代对象有:列表(list)、元组(tuple)、字典(dict)和文件对象等等。下面代码中(代码3),在MyList类中实现了__iter__方法,所以对象obj_list是可迭代对象(iterable),但对象obj_list不是迭代器(iterator)。虽然对象obj_list是可迭代对象(iterable),但它却不能被迭代(遍历)。
# 代码3
from collections.abc import Iterable, Iteratorclass MyList:def __init__(self,*args) -> None:self.data = list(args)def __iter__(self):print("__iter__被执行了...")return selfobj_list = MyList(2,4,6)
print(isinstance(obj_list, Iterable)) # 输出:True
print(isinstance(obj_list, Iterator)) # 输出:False
4. 迭代器(iterator)
在Python中,如果一个对象同时实现了__iter__
方法和__next__
方法,那它就是迭代器。也就是说,迭代器是一个实现了__iter__
和__next__
方法的对象。__iter__
方法返回迭代器对象自身,而 __next__
方法返回下一个元素。换句话说,迭代器是一个可以逐个返回元素的对象。如果在python的类中定义了__next__
和__iter__
方法,生成的实例对象可以通过for循环遍历来取,并且先调用__iter__
方法,再调用__next__
方法。示例代码如下所示(代码4):
# 代码4
from collections.abc import Iterable, Iteratorclass MyList:def __init__(self,*args) -> None:self.data = list(args)def __iter__(self):print("__iter__被执行了...")return selfdef __next__(self):pass
obj_list = MyList(2,4,6)
print(isinstance(obj_list, Iterable)) # 输出:True
print(isinstance(obj_list, Iterator)) # 输出:True
由于我们在类中实现了__iter__
和__next__
方法,所以对象obj_list既是可迭代对象(iterable),又是一个迭代器(iterator)。由此可得出一个结论:迭代器(iterator)一定是可迭代对象(iterable)。
5. 自定义迭代器
代码如下所示(代码5):
from collections.abc import Iterable,Iteratorclass MyList:def __init__(self,*args) -> None:self.data = list(args)self.start = 0def __iter__(self):print("__iter__被执行了...")return selfdef __next__(self):print("__next__被执行了...")if self.start >= len(self.data):# raise StopIteration用于提前终止一个迭代器中的循环raise StopIterationitem = self.data[self.start]self.start += 1return itemobj_list = MyList(2,4,6)
print(isinstance(obj_list,Iterable)) # 输出:True
print(isinstance(obj_list,Iterator)) # 输出:Truefor i in obj_list:print(i)输出:
True
True
__iter__被执行了...
__next__被执行了...
2
__next__被执行了...
4
__next__被执行了...
6
__next__被执行了...
小结
(1)迭代器(iterator): 如果一个对象同时实现了__iter__
方法和__next__
方法,那它就是迭代器;
(2)可迭代对象(iterable): 如果一个对象实现了__iter__
方法,那么这个对象就是可迭代对象。通常__iter__
方法需要返回一个实现了__next__
方法的对象,如果自己实现了,可以返回self
,当然这个返回值不是必须的。
(3)两者之间的关系:迭代器(iterator)一定是可迭代对象(iterable),反之则不成立。可迭代对象的__iter__
方法必须返回一个迭代器。
参考文章:一文看懂python的迭代器和可迭代对象
6. 生成器(generator)
在 Python 中,使用了关键字yield
的函数被称为生成器。生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,简单点说生成器就是一个迭代器。
生成器的关键在于yield
语句。yield
语句的作用和return
语句有几分相似,都可以将结果返回。不同在于,生成器函数执行到yield
语句,返回结果的同时记录下函数内的状态,下次执行这个生成器函数,将从上次退出的位置(yield的下一句代码)继续执行。当生成器函数中的所有代码被执行完毕时,自动抛出 StopIteration
异常。
(1)生成器函数:生成器的用法和迭代器相似,都可以使用 next()
函数来进行迭代。这是因为生成器其实就是创建迭代器的便捷方法,生产器会在背后自动定义 __iter__
和 __next__
方法。
# 生成器函数
def my_generator():for i in range(4):yield ia = my_generator()
print(a) # 输出:<generator object gen at 0x0000029481B899C8>
print(type(a)) # 输出:<class 'generator'>
print(next(a)) # 输出:0
print(next(a)) # 输出:1
print(next(a)) # 输出:2
print(next(a)) # 输出:3
print(next(a)) # 输出:StopIteration
(2)生成器表达式:生成器 = (关于item的表达式 for item in 可迭代对象)
my_generator = (item*2 for item in [2,4,6])
print(type(my_generator)) # 输出:<class 'generator'># 使用for循环遍历生成器
for i in my_generator:print(i) # 输出:4,8,12print(next(my_generator)) # 输出:4
print(next(my_generator)) # 输出:8
print(next(my_generator)) # 输出:12
print(next(my_generator)) # 输出:StopIteration
参考文章:Python进阶干货速递!【超详细迭代器、生成器、装饰器使用教程】