新闻详情

新闻详情

首页 / 资讯中心 / 详情

迭代器与生成器

发布时间:2026/9/28 4:51:47来源:尧图网络
迭代器与生成器
可迭代对象能被 for 循环遍历的对象被称为可迭代对象iterable可迭代对象iterable能调用 __iter__ 方法迭代器调用 __iter__ 方法会得到迭代器iterator备注1__iter__ 方法是一个魔法方法当调用 iter 函数时__iter__ 会自动调用备注2可迭代器对象.__iter__() 等价于 iter(可迭代器对象)备注3如果 iter(obj) 能得到一个迭代器iterator那obj就是可迭代对象。如何判断一个对象是可迭代对象name [张三, 李四, 王五] #list_iterator object at 0x000001DA82AF05B0 print(name.__iter__()) #list_iterator object at 0x000001CFB81905B0 print(iter(name))迭代器iterator拥有 __next__ 方法每次调用都会根据当前的状态返回下一个元素备注1迭代器.__next__() 等价于 next(迭代器)备注2当所有元素全都取出后若继续调用 __next__ 方法Python会抛出 StopIteration 异常。#可迭代对象 name [张三, 李四, 王五] #迭代器 it iter(name) print(next(it))#张三 print(next(it))#李四 print(next(it))#王五 print(next(it))#抛出 StopIteration 异常 # print(it.__next__())#张三 # print(it.__next__())#李四 # print(it.__next__())#王五 # print(it.__next__())#抛出 StopIteration 异常for 循环背后的工作逻辑#可迭代对象 name [张三, 李四, 王五] #编写for循环遍历names列表 for item in name: print(item)#for循环背后的逻辑 #调用可迭代器对象的__iter__方法获得一个迭代器 it iter(name) # 开启一个无限循环 while True: try: #调用迭代器的__next__方法获取下一个元素 item next(it) print(item) except StopIteration: #捕获StopIteration异常结束循环 break迭代器iterator也拥有 __iter__ 方法并且其返回值是迭代器本身这么设计的原因如下让 for 循环也能遍历迭代器即为了让 iter(迭代器)不出错#可迭代对象 name [张三, 李四, 王五] it iter(name) print(it)#list_iterator object at 0x0000019DC7A205B0 res1 iter(it) print(res1)#list_iterator object at 0x0000019DC7A205B0 res2 iter(res1) print(res2)#list_iterator object at 0x000001F895D805B0 #正常遍历 for item in it: print(item)迭代器协议能被 iter() 接受能被 next() 一步一步取值符合上面两个条件就符合 迭代器协议就是迭代器。手写迭代器特性迭代器是一次性的状态只会向前推进且不会自动重置迭代器在遍历过程中会被“消耗”#可迭代对象 name [张三, 李四, 王五] it1 iter(name) # 状态只会向前推进 print(next(it1)) print(next(it1)) print(next(it1)) # 由于迭代器不会自动重置需要重新获取迭代器 it2 iter(name) print(next(it2)) print(next(it2)) print(next(it2))注意点name [张三, 李四, 王五] it3 iter(name) for i in it3: print(i) #由于不会自动重置这里不会遍历直接抛异常退出循环了 for i in it3: print(i)实现迭代器需求让 for 循环可以遍历 Person 类的实例对象实现方式一实现两个类一个是可迭代器对象的类型另一个迭代器类。注意点迭代器中需要保存好外部传进来的数据迭代器中需要维护一个状态指针位置迭代器需要配置好遍历内容迭代器的实现需要符合迭代器协议即实现__iter__和__next__方法class Person: def __init__(self,name, age, gender, address): self.name name self.age age self.gender gender self.address address def __iter__(self): #返回一个迭代器 return PersonIterator(self) #实现Person迭代器类 class PersonIterator: def __init__(self,p): #将外部传进来的数据保存好 self.p p #设置迭代器初始化状态指针位置 self.index 0 #配置好要遍历的内容 self.addr [p.name, p.age, p.gender, p.address] # 迭代器的__iter__方法返回迭代器自身 def __iter__(self): return self #每次调用__next__方法会根据当前状态返回下一个元素 def __next__(self): #如果迭代器当前状态超出范围那就抛出StopIteration异常 if self.index len(self.addr): raise StopIteration #获取返回内容 val self.addr[self.index] #更新迭代器状态指针位置 self.index 1 return val p1 Person(张三, 18, 男, 上海) for item in p1: print(item)实现方式二使用一个类。该类即是 可迭代器对象又是 迭代器。注意点实现了迭代器协议就是 迭代器可以调用iter方法返回迭代器就是可迭代器对象。class Person: def __init__(self,name, age, gender, address): self.name name self.age age self.gender gender self.address address #需要设置为私有属性不然实例对象可以访问 # 设置迭代器初始化状态指针位置 self.__index 0 # 配置好要迭代器的内容 self.__addr [name,age,gender,address] #迭代器的__iter__方法返回迭代器自身 def __iter__(self): #这里是为了可以在外部多次变量Person的实例对象 #但是会一个bug外部也可以多次遍历Person迭代器 self.__index 0 return self #每次调用__next__方法会根据当前状态返回下一个元素 def __next__(self): #如果迭代器当前状态超出范围那就抛出StopIteration异常 if self.__index len(self.__addr): raise StopIteration #获取返回内容 val self.__addr[self.__index] #更新迭代器状态指针位置 self.__index 1 return val #Person的实例对象即是可迭代器对象 又是 迭代器 p1 Person(张三, 18, 男, 上海) for item in p1: print(item) for item in p1: print(item) it iter(p1) for item in it: print(item) #这里也会打印值 for item in it: print(item)进阶迭代器玩的就是__next__。看需求实现__next__方法。比如将变量元素全部转为大写将数字转为中文的__next__方法的实现。迭代器的优势迭代器是惰性计算不会一次性生成所有结果所以可以显著降低内存占用。当数据量很大不确定用多少结果时推荐使用迭代器。使用迭代器时返回的只是一个迭代器对象数据还没有生成只有在调用__next__方法的时候才生成了数据。#使用迭代器实现斐波那契数列 class Fibo: def __init__(self, total): self.total total self.index 0 self.prev 1 self.curr 1 def __iter__(self): return self def __next__(self): if self.index self.total: raise StopIteration if self.index 1: num 1 else: num self.prev self.curr self.prev self.curr self.curr num self.index 1 return num #f1是一个迭代器也是一个可迭代器对象 #此时还没有生成数据只是返回了一个对象 f1 Fibo(10) #在调用for循环时调用__next__才生成数据 for item in f1: print(item) # 使用函数实现斐波那契数列 def fibo(total): if total 0: return [] if total 1: return 1 nums [1, 1] for i in range(2, total): nums.append(nums[-1] nums[-2]) return nums #调用函数时数据已经生成了 f2 fibo(10) print(f2)#[1, 1, 2, 3, 5, 8, 13, 21, 34, 55] import tracemalloc tracemalloc.start() f3 Fibo(100000) m3 tracemalloc.get_traced_memory()[1] print(f占用内存{m3 / 1024 / 1024}MB)#占用内存0.00030517578125MB tracemalloc.start() f4 fibo(100000) m4 tracemalloc.get_traced_memory()[1] print(f占用内存{m4 / 1024 / 1024}MB)#占用内存444.99241638183594MB使用迭代器数据没有全部生成而是在调用__next__函数时才生成。生成器生成器函数与生成器对象生成器函数函数体中如果出现了 yield 关键字那该函数是【生成器函数】生成器对象调用【生成器函数】时其函数体不会立刻执行而是返回一个【生成器对象】验证调用生成器函数函数体不会执行而是返回一个生成器对象。注意点不管能否执行到 yield 所在的位置只要函数中有 yield 关键字那该函数就是【生成器函数】如何执行写在【生成器函数】中的代码需要通过【生成器对象】来执行调用【生成器对象】的__next__方法会让【生成器函数】中的代码开始执行。当【生成器函数】中的代码开始执行后遇到 yield 会‘暂停’执行并且其内部会记录‘暂停’位置。后续调用 __next__ 方法时都会从上一次‘暂停’位置继续执行直到再次遇到 yield。遇到 return 会抛出 StopIteration 异常并将 return 后面的表达式作为异常信息。yield 后面的表达式会作为本次 __next__ 方法的返回值。生成器对象的细节生成器对象是一种特殊的迭代器本质是通过 yield 关键字自动实现了迭代器协议。验证生成器对象是迭代器for循环遍历生成器yield也能写在循环里yield from 能把一个【可迭代对象】里的元素依次 yield 出去。替代for yield使用生成器.send(值)可以让生成器继续执行的同时给上一次 yield 传值。next 只能取值send既能取值也能送值。第一次启动生成器不能传值因为第一次没有上一个yield。但是可以传None因为等价于next方法。生成器.next() 等价于 生成器.send(None)用生成器实现两个效果主要是利用了 yield 关键字Python就会自动实现迭代器协议。返回一个迭代器(生成器对象)。用生成器实现一个可以使用for循环遍历的Person类函数实现斐波那契数列无论是迭代器还是生成器对象都可以用listtupleset等直接拿到其里面的内容注意元素多可能挤爆内存生成器虽然实现某些需求代码量比迭代器少因为自动实现了迭代器协议。但是当有自定义需求时还是得通过迭代器来实现即自定义实现__next__方法。生成器表达式一种用类似列表推导式的语法快速创建生成器对象的方式。语法格式(表达式 for 变量 in 可迭代对象)什么时候适合用生成器表达式—— 当“每个结果只依赖当前这一个元素”时。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

PHP实现sendfile零拷贝:大文件下载CPU直降的实战方案 2026/9/28 5:49:54

PHP实现sendfile零拷贝:大文件下载CPU直降的实战方案

接手那个下载服务的时候,我完全没想到问题出在“搬家”上。1GB 的安装包每天被拉走几十万次,16 核机器 CPU 一路飙到 80%,top 里清一色 php-fpm 进程。第一反应是带宽不够,可网卡明明只跑到 300Mbps;直到我 strace 挂上…

阅读更多 →
Linux Socket内核探秘:从文件描述符到epoll排查实战 2026/9/28 5:49:54

Linux Socket内核探秘:从文件描述符到epoll排查实战

这两年我在排查线上问题的时候,有好几次都是被Linux下的Socket异常状态折磨到深夜。印象最深的一次是凌晨一点,业务同学反馈某个接口偶发Connection reset by peer,代码层面找不到任何异常,日志里就孤零零一行错误。折腾半天之后我…

阅读更多 →
Python全栈实战:爬虫采集交通数据,Flask+SQLAlchemy+ECharts构建可视化大屏 2026/9/28 5:49:54

Python全栈实战:爬虫采集交通数据,Flask+SQLAlchemy+ECharts构建可视化大屏

从爬虫抓数据到Flask后端,再到数据库落库,最后在一张酷炫的大屏上把所有交通信息展示出来——这套基于Python车联网数据系统的完整闭环,我前后折腾了将近一个月,踩了不少坑。今天这篇就把整个构建思路、核心代码、以及那些常规文档…

阅读更多 →
3步搞定百度域名地址,避开建站报价坑 2026/9/28 5:49:54

3步搞定百度域名地址,避开建站报价坑

3步搞定百度域名地址,避开建站报价坑 网站被黑挂马,后台突然多了几百个垃圾链接,甚至直接跳转到博彩网站?这时候你才慌,但慌没用。很多老板这时候才想起来问: 建站报价 到底包含啥?是不是只要交了钱,后面出事了就得自己扛?…

阅读更多 →
网站网页设计怎么报价?老鸟揭秘行业底价避坑指南 2026/9/28 5:49:53

网站网页设计怎么报价?老鸟揭秘行业底价避坑指南

网站网页设计怎么报价?老鸟揭秘行业底价避坑指南 别再被“模板网站太丑不够用”这句话骗了,很多甲方拿着3999元的模板站去对标5万级的定制站,最后做出来的东西既没质感又跑不快,这才是最大的坑。…

阅读更多 →
Java+Vue电池销售管理系统实战:前后端分离、库存事务与部署全解析 2026/9/28 5:49:47

Java+Vue电池销售管理系统实战:前后端分离、库存事务与部署全解析

1. 先搞清楚这是一套什么样的系统1.1 电池销售业务的真实痛点在哪每年一到课程设计或者毕业设计的季节,就能看到大量同学在到处找管理系统源码。Java Vue 的组合这两年特别火,因为它既有后端的业务逻辑、事务处理、数据库设计,又有前端的页面…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉