python之迭代器与生成器
python之迭代器与生成器
可迭代
假如现在有一个列表,有一个int类型的12345。我们循环输出。
1 2 3 4 5 | list=[1,2,3,4,5] for i in list: print(i) for i in 12345: print(i) |
结果:
1 2 3 4 5 6 7 8 9 | Traceback (most recent call last ): File "C:/Pycham/生成器与迭代器/test1.py" , line 6, in <module> for i in 12345: TypeError: 'int' object is not iterable 1 2 3 4 5 |
1 2 3 | 报错显示:1234不是可以被迭代的。 那python中哪些是<strong>可迭代</strong>的:字符串、列表、元组、字典、集合。 |
1 | for 循环工作机制: for 循环在循环一个对象的时候,会调用这个对象的 iter 方法,得到迭代器,然后在调用这个迭代器的 next 方法,去获得这个迭代器中包涵的每个值。<br>现在可能我们不太明白,什么是 iter 方法,什么是迭代器,什么是 next 方法。 |
迭代器
1 2 3 | 但是如果只是将数据集内的数据“一个挨着一个的取出来, for 循环就可以做到,为什么要使用迭代器呢?迭代器是什么? <strong>迭代器能迭代的一定是可以迭代的数据类型。</strong> 如果我们要使用迭代器,一定要将可以被迭代的数据集转为迭代器,使用<strong>__iter__()</strong>。 |
1 2 3 4 5 6 7 8 9 10 | #列表生成式 list=[1,2,3,4,5] #生成器 gen=list.__iter__() print(list) print(gen) 结果: [1, 2, 3, 4, 5] <list_iterator object at 0x0000000002627278> |
迭代器的三个方法
1 2 3 4 5 6 7 | iter_l = [1,2,3,4,5,6].__iter__() #获取迭代器中元素的长度 print(iter_l.__length_hint__()) #根据索引值指定从哪里开始迭代 print(iter_l.__setstate__(2)) #一个一个的取值 print(iter_l.__next__()) |
1 2 3 | 结果:<br>6 None 3 |
循环输出迭代器的内容
注意:
很重要的特性,就是不可逆,只能前进,不能后退。
如果迭代的次数超过里面的数据,就会报错。
1 2 3 4 5 6 7 8 | l = [ 1 , 2 , 3 , 4 ] l_iter = l.__iter__() while True : try : item = l_iter.__next__() print (item) except StopIteration: break |
1 2 3 4 5 | 总结:一个对象是否可迭代,全都取决于这个对象是否有 iter 方法,调用对象的 iter 方法,就回返回一个迭代器,这个迭代器一定具有 next 方法,在调用这个迭代器的 next 方法时,迭代器就回返回它的下一个值,当迭代器中没有 值可以返回了,就回抛出一个名为StopIteration的异常,停止迭代。 for 循环的工作机制,可以让我们遍历任何一个可迭代的数据集。 虽然序列类型字符串,列表,元组都有下标,你用下标的方式访问。 但是非序列类型像字典,集合,文件对象这样的数据类型也是可迭代的。 |
生成器
1 2 3 4 5 6 7 8 9 10 | 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含 100 万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的 list ,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator 生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用 yield 返回值函数,每次调用 yield 会暂停,而可以使用 next ()函数和send()函数恢复生成器。 生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像<br>是迭代器。 <strong>总结:生成器是个比较特殊的可迭代对象,它与其他的可迭代对象不太一样的地方,其他的可迭代对象需要调用 iter 方法,返回个迭代器对象,然后通过迭代器对象去执行 next 方法,获取迭代器中的值,但是生成器直接可以被迭代,无需执行 iter 方法。 < / strong> |
1 2 3 4 5 6 | 生成器Generator: 本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现) 特点:惰性运算,开发者自定义 |
初始生成器
import time def genrator_fun1(): a = 1 print('现在定义了a变量') yield a b = 2 print('现在又定义了b变量') yield b g1 = genrator_fun1() print('g1 : ',g1) #打印g1可以发现g1就是一个生成器 print('-'*20) #我是华丽的分割线 print(next(g1)) time.sleep(1) #sleep一秒看清执行过程 print(next(g1))
python中生成器有两种表达形式
1 | 函数式生成器:在常规的函数中定义的生成器,语句的返回值不再使用 return 去返回,而是使用 yield 关键字每次返回一个结果,一个函数中不可以有多个 return ,但是可以有多个 yield ,函数中的每一个 yield 都会返回一个结果,每执行一个 yield ,函数的执行状态都会被‘挂起’可以理解<br>为暂停,下次继续调用这个函数的时候,会从上次挂起的位置继续向下执行。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def func1(): yield 1 print ( "第一个yield执行完成~" ) yield 2 print ( "第二个yield执行完成~" ) yield 3 print ( "第三个yield执行完成~" ) for i in func1(): print (i) |
1 2 3 4 5 6 | 结果:<br> 1 第一个 yield 执行完成~ 2 第二个 yield 执行完成~ 3 第三个 yield 执行完成~ |
生成器表达式:使用类似于列表推导式的方法,但是返回的对象不再是一个列表,而是一个可以按需生成结果的一个对象(生成器)。
只要把一个列表生成式的[]中括号改为()小括号,就创建一个generator
1 2 3 4 5 6 7 8 9 10 | #列表生成式 lis = [x * x for x in range ( 10 )] print (lis) #生成器 generator_ex = (x * x for x in range ( 10 )) print (generator_ex) 结果: [ 0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 ] <generator object <genexpr> at 0x000002A4CBF9EBA0 > |
for循环输出生成器的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #生成器 generator_ex = (x * x for x in range ( 10 )) for i in generator_ex: print (i) 结果: 0 1 4 9 16 25 36 49 64 81 |
所以我们创建一个generator后,基本上永远不会调用next(),而是通过for循环来迭代,并且不需要关心StopIteration的错误(迭代超过长度)。
1 2 3 | 使用生成器的优点: 延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。 |
最后示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import time def cumtom(name): print ( '%s准备吃包子' % name) time.sleep( 1 ) while 1 : count = yield print ( '%s吃到第%d个包子' % (name,count)) def producter(): con1.__next__() con2.__next__() n = 1 while 1 : time.sleep( 1 ) print ( '已经生产出来%d、%d个包子' % (n,n + 1 )) #通过send方法通知 con1.send(n) con2.send(n + 1 ) n + = 2 con1 = cumtom( 'cumtom1' ) con2 = cumtom( 'cumtom2' ) producter() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | cumtom1准备吃包子 cumtom2准备吃包子 已经生产出来 1 、 2 个包子 cumtom1吃到第 1 个包子 cumtom2吃到第 2 个包子 已经生产出来 3 、 4 个包子 cumtom1吃到第 3 个包子 cumtom2吃到第 4 个包子 已经生产出来 5 、 6 个包子 cumtom1吃到第 5 个包子 cumtom2吃到第 6 个包子 已经生产出来 7 、 8 个包子 cumtom1吃到第 7 个包子 cumtom2吃到第 8 个包子 已经生产出来 9 、 10 个包子 cumtom1吃到第 9 个包子 cumtom2吃到第 10 个包子 已经生产出来 11 、 12 个包子 cumtom1吃到第 11 个包子 cumtom2吃到第 12 个包子 已经生产出来 13 、 14 个包子 cumtom1吃到第 13 个包子 cumtom2吃到第 14 个包子 已经生产出来 15 、 16 个包子 cumtom1吃到第 15 个包子 cumtom2吃到第 16 个包子 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?