详解Python3中的迭代器和生成器及其区别
作者:miuric 发布时间:2022-11-01 00:37:48
介绍
本篇将介绍Python3中的迭代器与生成器,描述可迭代与迭代器关系,并实现自定义类的迭代器模式。
迭代的概念
上一次输出的结果为下一次输入的初始值,重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值
注:循环不是迭代
while True: #只满足重复,因而不是迭代
print('====>')
迭代器
1.为什么要有迭代器?
对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式。
2.迭代器定义:
迭代器:可迭代对象执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法
它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常
可迭代的(iterable)
Python标准库中存在着一些可迭代对象,例如:list, tuple, dict, set, str等。
可以对这些迭代对象,进行for-in等迭代操作,例如:
for s in "helloworld":
print(s)
编译器若想迭代一个对象a,则会自动调用iter(a)获取该对象的迭代器(iterator),如果iter(a)抛出异常,则对象a不可迭代。
判断对象是否可迭代
原生函数iter(instance) 可以判断某个对象是否可迭代,它的工作流程大概分为以下3个步骤:
检查对象instance是否实现了__iter__方法,并调用它获取返回的迭代器(iterator)。
如果对象没有实现__iter__方法,但是实现了__getitem__方法,Python会生成一个迭代器。
如果上述都失败,则编译器则抛出TypeError错误,‘xxx' Object is not iterable。
自定义类实现__iter__方法
根据第一条,我们自定义类Iter1实现__iter__方法使该类的对象可迭代。
class Iter1:
def __init__(self, text):
self.text = text
def __iter__(self):
return iter(self.text)
iter1 = Iter1("hello")
for s in iter1:
print(s)
Iter1类实现了__iter__方法,通过iter()调用,得到可迭代对象text的迭代器并返回,实现了迭代器协议,因此可以通过for-in等方式对该对象进行迭代。
第二条通常都是针对Python中的序列(sequence)而定义,例如list,为了实现sequence协议,需要实现__getitem__方法。
class Iter2:
def __init__(self, sequence):
self.sequence = sequence
def __getitem__(self, item):
return self.sequence[item]
iter2 = Iter2([1, 2, 3, 4])
for s in iter2:
print(s)
实际上,为了避免版本后序改动,Python标准库中的序列除了实现了__getitem__方法,也实现了__iter__方法,因此我们在定义序列时也应实现__iter__。
综上,如果显示判断某个对象是否可迭代,应该调用iter(instance)是否抛出异常,因为只实现了__getitem__的序列也是可迭代的(例子中Iter2的对象是可迭代的,但isinstance(iter2, abc.Iterator)返回结果是False)。同时,如果在调用iter后进行迭代操作不必显示判断,可以用try/except方式包装代码块。
iterable vs iterator(可迭代vs迭代器)
iterable定义
任何可以由原生函数iter获取到迭代器的对象
任何实现了__iter__方法并返回迭代器的对象
所有的序列(实现了__getitem__)
Python通过获取到可迭代对象的迭代器(iterator)实现迭代,例如for-in的实现其实是在内部获取到了迭代器进行操作。for-in机制可以理解为下述代码:
s = 'hello'
it = iter(s)
while (True):
try:
print(next(it))
except StopIteration:
del it
break
StopIteration异常将在迭代器耗尽后被抛出,for-in、生成式(comprehension)、元组解压(tuple unpacking)等迭代操作都会处理并这个异常。
迭代器是个迭代值生产工厂,它保存迭代状态,并通过next()函数产生下一个迭代值。实现迭代器需要实现以下两个方法:
__iter__
返回self
__next__
返回下一个可用的元素,如果无可用元素则抛出StopIteration异常
迭代器实现__iter__,因此所有的迭代器都是可迭代的,下图展示了iterable和iterator的结构。
迭代器模式
实现一个自定义的迭代器模式需要两个类,分别为实现了__iter__方法的类和通过__iter__返回的迭代器实例类(实现了__iter__和__next__方法)。下面例子简单实现了上述功能。
class IterText:
def __init__(self, text):
self.text = text
def __iter__(self):
return IteratorText(self.text)
class IteratorText:
def __init__(self, text):
self.text = text
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
letter = self.text[self.index]
except IndexError:
raise StopIteration
self.index += 1
return letter
text = IterText("hey")
for l in text:
print(l)
可迭代的IterText实现了__iter__方法,返回了迭代器IteratorText实例。IteratorText实现了__next__方法返回下一个迭代元素直到抛出异常,同时IteratorText实现了__iter__方法返回自身对象用于迭代。
这里的IterText和IteratorText很容易混淆,如果在IterText中实现了__next__方法并将__iter__中返回自身实例self也可以实现上述功能,但通常可迭代对象和迭代器应当分开,这样在可迭代对象中的__iter__中可以返回不同的迭代器对象,使功能独立。
生成器(generator)
通过上述文章说明,迭代器通过next()不断产出下一个元素直到迭代器耗尽,而Python中的生成器可以理解为一个更优雅的迭代器(不需要实现__iter__和__next__方法),实现了迭代器协议,它也可以通过next()产出元素。
Python中的生成器主要分为两种类型:
生成器函数(generator function)返回得到的生成器:
包含yield关键字的函数称为生成器函数
def gen_func():
yield 1
yield 2
yield 3
g = gen_func()
生成器表达式(generator expression)返回得到的生成器
g = (i for i in (1, 2, 3))
我们可以利用生成器进行迭代操作:
for e in g:
print(e)
## 生成器g已被耗尽,如果需要重新迭代需要重新获得新的生成器对象
g = gen_func()
for e in g:
print(e)
利用生成器代替可迭代中的__iter__迭代器
在迭代器模式章节中,我们在可迭代IterText中的__iter__返回迭代器IteratorText实例,然而使用生成器的方式会使代码更加优雅。
class IterText:
def __init__(self, text):
self.text = text
def __iter__(self):
for letter in self.text:
yield letter
因为yield存在于__iter__,因此__iter__变成了生成器函数,调用它测返回一个生成器,同时生成器又实现了迭代器协议,因此IterText满足了可迭代的需求。
总结
本篇介绍了Python中的可迭代(iterable)、迭代器(iterator)以及它们的关系,并讲述了迭代器模式的实现,同时通过Python中的生成器完善了迭代器模式。希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
来源:https://segmentfault.com/a/1190000016620127
猜你喜欢
- Python time模块时间获取和转换Time模块介绍Python的Time库可以进行时间相关的处理,如访问当前日期和时间,输出不同格式的
- 在上一篇《服务器XMLHTTP(Server XMLHTTP in ASP)基础》中我介绍了一下ServerX
- 效果图:css:<style type="text/css"> /* 带复选框的下拉框 */ ul li{
- Python引入了一个机制:引用计数。引用计数python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,
- 英文原文:The seven rules of Unobtrusive JavaScript原文地址:http://icant.co.uk/
- 一般在Django的视图函数中使用request.POST来获取请求体,request.POST是QueryDict类,通常作为dict来使
- 有多少次你在考虑怎样设置数据库时感到为难?其实,如果你在Linux上使用MySQL,就不会有这种情况了。在Linux上使用Webmin图形界
- 今天刚看到ChinaUI.com新改版的网站,觉得优艾网是越来越漂亮了,这次的变化的确是挺大的。一早大概的看了一下就关了,后来晚上又打开看时
- 本文实例为大家分享了python实现图像拼接的具体代码,供大家参考,具体内容如下1.待拼接的图像2. 基于SIFT特征点和RANSAC方法得
- 有些时候(如开发聊天程序),我们需要将将滚动条(scrollbar)保持在最底部,比如聊天窗口,最新发出和收到的信息要显示在最下方,如果要看
- TEMPLATESDjango 1.8的新特性一个列表,包含所有在Django中使用的模板引擎的设置。列表中的每一项都是一个字典,包含某个引
- CSS Sprites技术不新鲜,早在2005年 CSS Zengarden 的园主 Dave Shea 就在 ALA
- 作为一位不懂代码的业余网页制 * 好者,常常羡慕专业程序人员在浏览器中编制出的效果超酷的一些多媒体作品。唉,无奈程序那东东,酶涩南学,非一日之
- 本次做一个最简单的贪食蛇雏形游戏,就是一个小蛇在画面上移动,我们可以控制蛇的移动方向,但是不能吃东西,蛇不会长大。但是基础的有了,完整版的贪
- css的流行导致了标签的流行,很直观,看起来很清爽。流行的一部分,还有很多种功能强大且美观的导航。。。1. Change.org2. N.D
- 为 Web页指定 DOCTYPE 会影响浏览器呈现页的方式。Internet Explorer、Mozilla Firefox 和 Oper
- Python中对象的行为是由它的类型 (Type) 决定的。所谓类型就是支持某些特定的操作。数字对象在任何编程语言中都是基础元素,支持加、减
- 如何阻止别人非法链接你网站的图片,防盗链?getimage.asp<% Option ExplicitDim&nb
- PyType_Type和PyBaseObject_TypePyObject和PyTypeObject内容的最后指出下图中对实例对象和类型对象
- 阅读上一篇:什么是名字空间<meta http-equiv="Content-Type" co