迭代器?
你有没有想过在python里for i in lit遍历一个列表,他究竟干了什么,为什么有的变量可以循环,而有的不可以for遍历?就比如说for i in 2,对一个数字遍历会报错
TypeError: 'int' object is not iterable,这句话意思是int对象不是迭代器,看来想要迭代,还必须要有一定的属性,而这个属性就是__iter__以及__next__成员方法。
现在我用一个学生对象来举例迭代器,假设我想用for循环打印该学生的所有属性值:
- 第一步:对学生类创建__iter__成员方法
该方法用于返回迭代器,你可以直接返回一个可迭代对象比如list或元组等内置对象,也可以返回self自己本身,只不过返回self本身,你就需要写__next__函数的逻辑。 - 第二步:添加__next__方法
如果返回self本身作为迭代器就需要__next__方法,这个方法用于填写返回值的逻辑以及什么时候遍历终止。在迭代完毕时,需要抛出StopIteration来告知没有元素可以迭代!
classStudent:def__init__(self,name,age,stu_id,grade):# 初始化学生属性self.name=name# 姓名self.age=age# 年龄self.stu_id=stu_id# 学号self.grade=grade# 成绩def__iter__(self):# 迭代器初始化方法:返回迭代器对象本身# 定义一个索引,用来遍历属性列表self.index=0# 把对象的所有属性存入列表(固定遍历顺序)self.attributes=[self.name,self.age,self.stu_id,self.grade]returnselfdef__next__(self):# 迭代器核心:每次调用返回下一个属性ifself.index<len(self.attributes):# 获取当前属性value=self.attributes[self.index]# 索引+1,为下一次迭代做准备self.index+=1returnvalue# 迭代完毕,抛出停止迭代异常raiseStopIteration# 测试代码if__name__=='__main__':# 创建学生对象stu=Student("张三",18,"2025001",95)# 迭代遍历学生所有属性(for循环自动调用迭代器)print("学生所有属性:")forattrinstu:print(attr)# 手动调用迭代器(验证手写迭代器生效)print("\n手动迭代属性:")iter_obj=iter(stu)print(next(iter_obj))print(next(iter_obj))print(next(iter_obj))print(next(iter_obj))所以定义迭代器有什么用,只是遍历?
迭代器在读取文件时大有用途,比如读取一个大型文件,你的目的是一行一行读取,在需要时读取下一行,于是你认为读取到列表里是不错的选择,但是发现一下子加载到内存就会有很大开销,于是迭代器登场了:
classLineReader:def__init__(self,file_path):# 打开文件self.file=open(file_path,'r',encoding='utf-8')# 标记是否已经读取完毕self.is_finished=Falsedef__iter__(self):# 迭代器返回自身returnselfdef__next__(self):# 如果已经读完,直接抛出停止迭代ifself.is_finished:self.file.close()# 关闭文件raiseStopIteration# 读取一行line=self.file.readline()# 如果读到空字符串,说明文件结束ifnotline:self.is_finished=Trueself.file.close()raiseStopIteration# 去掉换行符并返回returnline.strip()# ===================== 测试 =====================if__name__=='__main__':# 先创建一个测试文件 test.txtwithopen('test.txt','w',encoding='utf-8')asf:f.write("第一行:我是学生\n")f.write("第二行:学习Python\n")f.write("第三行:手写迭代器\n")f.write("第四行:逐行读取文件\n")# 使用我们的逐行读取迭代器print("=== 逐行读取内容 ===")reader=LineReader("test.txt")# for 循环自动迭代forlineinreader:print(line)这个迭代器可以实现一行一行读取文件,而且内存开销也不大,读完时也会自动关闭文件。
生成器函数
如果上面读取文件的代码太复杂,生成器也是不错选择。
生成器函数里必须有yield,每次遍历生成器,yield都会返回一个生成的内容,并且函数阻塞到这里,等到下次调用该生成器,会从这里继续执行后面的代码。
生成器生成1-9的例子
deftest():a=1whilea<10:yielda a+=1foriintest():print(i)他的逻辑是这样的:for循环先从生成器拿到第一个值1,此时生成器阻塞在a+=1处,当for循环打印1后,再次从生成器取值,生成器从阻塞的地方继续执行,a+=1后yield返回2以此类推.注意下面是错误的写法:
deftest():a=1ifa<10:yielda a+=1foriintest():print(i)生成器遍历文件
defread_file(file_path):f=open(file_path,'r',encoding='utf-8')whileTrue:line=f.readline()ifnotline:breakyieldline f.close()# 遍历forlineinread_file("test.txt"):print(line)