您现在的位置是:网站首页> 编程资料编程资料
详解Python中迭代器和生成器的原理与使用_python_
2023-05-26
339人已围观
简介 详解Python中迭代器和生成器的原理与使用_python_
关于python中迭代器,生成器介绍的文章不算少数,有些写的也很透彻,但是更多的是碎片化的内容。本来可迭代对象、迭代器、生成器概念就很绕,又加上过于碎片的内容,更让人摸不着头脑。本篇尝试用系统的介绍三者的概念和关系,希望能够帮助需要的人。
1.可迭代对象、迭代器
1.1概念简介
迭代:
首先看迭代的字面意思:

迭代的意思就是:迭代是一种行为,反复执行的动作。在python中可以理解为反复取值的动作。
可迭代对象:顾名思义就是可以从里面迭代取值的对象,在python中容器类的数据结构都是可迭代对象,如列表,字典,集合,元组等。
迭代器:类似于从可迭代对象中取值的一种工具,严谨的说可以将可迭代对象中的值取出的对象。
1.2可迭代对象
在python中,容器类型的数据结构都是可迭代对象,列举如下:
- 列表
- 字典
- 元组
- 集合
- 字符串
>>> arr = ['圣僧','大圣','天蓬','卷帘'] >>> for i in arr: ... print(i) ... 圣僧 大圣 天蓬 卷帘 >>>
除了python自带的数据结构是可迭代对象之外,模块里的方法、自定义的类也可能是可迭代对象。那么如何确认一个对象是否为可迭代对象呢?有一个标准,那就是可迭代对象都有方法__iter__,凡是具有该方法的对象都是可迭代对象。
>>> dir(arr) ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
1.3迭代器
常见迭代器是从可迭代对象创建而来。调用可迭代对象的__iter__方法就可以为该可迭代对象创建其专属迭代器。使用iter()方法也可以创建迭代器,iter()本质上就是调用可迭代对象的__iter__方法。
>>> arr = ['圣僧','大圣','天蓬','卷帘'] >>> arr_iter = iter(arr) >>> >>> for i in arr_iter: ... print(i) ... 圣僧 大圣 天蓬 卷帘 >>> >>> >>> arr_iter = iter(arr) >>> next(arr_iter) '圣僧' >>> next(arr_iter) '大圣' >>> next(arr_iter) '天蓬' >>> next(arr_iter) '卷帘' >>> next(arr_iter) Traceback (most recent call last): File "", line 1, in StopIteration
可迭代对象只能通过for循环来遍历,而迭代器除了可以通过for循环来遍历,重要的是还可以通过next()方法来迭代出元素。调用一次迭代出一个元素,直到所有元素都迭代完,抛出StopIteration错误。这个过程就像象棋中没有过河的小卒子——只能前进不能后退,并且迭代完所有元素也无法再次遍历。
简单总结迭代器的特征:
- 可以使用
next()方法迭代取值 - 迭代的过程只能向前不能后退
- 迭代器是一次性的,迭代完所有元素就无法再次遍历,需要再次遍历只有新建迭代器
迭代器对象在python中很常见,比如打开的文件就是一个迭代器、map,filter,reduce等高阶函数的返回也是迭代器。迭代器对象拥有两个方法:__iter__ 和__next__。next()方法能迭代出元素就是调用__next__来实现的。
>>> dir(arr_iter) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
1.4区分可迭代对象和迭代器
如何区分可迭代对象和迭代器呢?在python的数据类型增强模块collections中有可迭代对象和迭代器数据类型,通过isinstance类型比较即可区分出两者。
>>> from collections import Iterable, Iterator >>> arr = [1,2,3,4] >>> isinstance(arr, Iterable) True >>> isinstance(arr, Iterator) False >>> >>> >>> arr_iter = iter(arr) >>> isinstance(arr_iter, Iterable) True >>> isinstance(arr_iter, Iterator) True >>>
arr:可迭代对象。是可迭代对象类型,不是迭代器类型
arr_iter:迭代器。既是可迭代对象类型,又是迭代器类型
1.5可迭代对象和迭代器的关系
从迭代器的创建就能大致看出。可迭代对象就是一个集合,而迭代器就是为这个集合创建的迭代方法。迭代器迭代时是直接从可迭代对象集合里取值。可以用如下模型来理解两者之间的关系:
>>> arr = [1,2,3,4] >>> iter_arr = iter(arr) >>> >>> arr.append(100) >>> arr.append(200) >>> arr.append(300) >>> >>> for i in iter_arr: ... print(i) ... 1 2 3 4 100 200 300 >>>
可以看到这里的流程是:
- 先创建可迭代对象arr
- 然后从arr创建的arr_iter迭代器
- 再向arr列表追加元素
- 最后迭代出来的元素包括后追加的元素。
可以说明迭代器并不是copy了可迭代对象的元素,而是引用了可迭代对象的元素。在迭代取值时直接使用了可迭代对象的元素。
1.6可迭代对象和迭代器的工作机制
首先整理一下两者的方法
可迭代对象: 对象中有__iter__ 方法
迭代器:对象中有__iter__ 和 __next__方法
在迭代器的创建时提到过__iter__方法是返回一个迭代器,__next__是从元素中取值。所以,关于两者方法的功能:
可迭代对象:
__iter__方法的作用是返回一个迭代器
迭代器:
__iter__方法的作用是返回一个迭代器,就是自己。
__next__方法的作用是返回集合中下一个元素
可迭代对象是一个元素集合,本身没有自带取值的方法,可迭代对象就像老话说的茶壶里的饺子,有货倒不出。
>>> arr = [1,2,3,4] >>> >>> next(arr) Traceback (most recent call last): File "", line 1, in TypeError: 'list' object is not an iterator
既然饺子倒不出来,又想吃怎么办?那就得找筷子一样的工具来夹出来对吧。而迭代器就是给用来给可迭代对象取值的工具。
给可迭代对象arr创建的迭代器arr_iter,可以通过next取值,将arr中值全部迭代出来,直到没有元素抛出异常StopIteration
>>> arr_iter = iter(arr) >>> >>> next(arr_iter) 1 >>> next(arr_iter) 2 >>> next(arr_iter) 3 >>> next(arr_iter) 4 >>> next(arr_iter) Traceback (most recent call last): File "", line 1, in StopIteration >>>
for 循环本质
>>> arr = [1,2,3] >>> for i in arr: ... print(i) ... 1 2 3
以上通过for循环遍历出arr中所有值。我们知道列表arr是可迭代对象,本身无法取值,for循环如何迭代出所有元素呢?
for循环的本质就是给arr创建一个迭代器,然后不断调用next()方法取出元素,复制给变量i,直到没有元素抛出捕获StopIteration的异常,退出循环。可以通过模拟for循环更直观的说明:
arr = [1,2,3] # 给arr生成一个迭代器 arr_iter = iter(arr) while True: try: # 不断调用迭代器next方法,并捕获异常,然后退出 print(next(arr_iter)) except StopIteration: break >> 1 2 3
到这里大概就讲完了可迭代对象和迭代器的工作机制,简单总结:
可迭代对象: 保存元素,但自身无法取值。可以调用自己的__iter__方法创建一个专属迭代器来取值。
迭代器:拥有__next__方法,可以从指向的可迭代对象中取值。只能遍历一遍,并且只能前进不能后退。
1.7自己动手创建可迭代对象和迭代器
榴莲好不好吃,只有尝一尝才知道。迭代器好不好理解,动手实现一次就清楚。下面自定义可迭代对象和迭代器。
如果自定义一个可迭代对象,那么需要实现__iter__方法;
如果要自定义一个迭代器,那么就需要实现__iter__和__next__方法。
可迭代对象:实现__iter__方法,功能是调用该方法返回迭代器
迭代器:实现__iter__,功能是返回迭代器,也就是自身;实现__next__,功能是迭代取值直到抛出异常。
from collections import Iterable, Iterator # 可迭代对象 class MyArr(): def __init__(self): self.elements = [1,2,3] # 返回一个迭代器,并将自己元素的引用传递给迭代器 def __iter__(self): return MyArrIterator(self.elements) # 迭代器 class MyArrIterator(): def __init__(self, elements): self.index = 0 self.elements = elements # 返回self,self就是实例化的对象,也就是调用者自己。 def __iter__(self): return self # 实现取值 def __next__(self): # 迭代完所有元素抛出异常 if self.index >= len(self.elements): raise StopIteration value = self.elements[self.index] self.index += 1 return value arr = MyArr() print(f'arr 是可迭代对象:{isinstance(arr, Iterable)}') print(f'arr 是迭代器:{isinstance(arr, Iterator)}') # 返回了迭代器 arr_iter = arr.__iter__() print(f'arr_iter 是可迭代对象:{isinstance(arr_iter, Iterable)}') print(f'arr_iter 是迭代器:{isinstance(arr_iter, Iterator)}') print(next(arr_iter)) print(next(arr_iter)) print(next(arr_iter)) print(next(arr_iter)) 结果:
arr 是可迭代对象:True
arr 是迭代器:False
arr_iter 是可迭代对象:True
arr_iter 是迭代器:True
1
2
3
Traceback (most recent call last):
File "myarr.py", line 40, in
print(next(arr_iter))
File "myarr.py", line 23, in __next__
raise StopIteration
StopIteration
从这个列子就能清晰的认识可迭代对象的迭代器的实现。可迭代对象的__iter__方法返回值就是一个实例化的迭代器的对象。这个迭代器的对象保存了可迭代对象的元素的引用,也实现了取值的方法,所以可以通过next()方法取值。这是一个值得细品的代码,比如说有几个问题可以留给读者思考:
- 为什么next()只能前进不能后退
- 为什么迭代器只能遍历一次就失效 <
相关内容
- python神经网络Keras搭建RFBnet目标检测平台_python_
- 如何对numpy 矩阵进行通道间求均值_python_
- Python中itertools模块的使用教程详解_python_
- 使用numpy.mean() 计算矩阵均值方式_python_
- python利用opencv调用摄像头实现目标检测_python_
- Pandas 如何处理DataFrame中的inf值_python_
- python之NAN和INF值处理方式_python_
- mAP计算目标检测精确度实现源码_python_
- Python判断Nan值的五种方式小结_python_
- Python图片视频超分模型RealBasicVSR的使用教程_python_


