Python迭代器协议与for循环底层机制深度解析
本文深入解析Python中迭代器协议的核心概念,阐明for循环如何通过__iter__和__next__方法遍历对象。详细对比生成器函数与表达式在内存优化和延迟计算上的优势,并通过代码示例展示生成器表达式相比列表推导式在节省内存方面的实际效果,帮助开发者理解Python数据访问的底层逻辑。
递归与迭代概述
一、递归与迭代
迭代器协议详解
二、什么是迭代器协议
1、迭代器协议的定义:对象必须实现一个next方法(在Python 3中为__next__),调用该方法时,若存在下一项则返回该项,否则触发StopIteration异常以终止迭代。此过程单向进行,不支持回退操作。
2、可迭代对象:指实现了迭代器协议的对象。具体实现方式是在对象内部定义__iter__()方法,该方法返回一个迭代器对象。
3、协议作为一种约定,当可迭代对象实现了迭代器协议后,Python内部工具(如for循环、sum、min、max等内置函数)便依据该协议来访问对象。
for循环底层机制
三、Python中强大的for循环机制
for循环的本质:遍历所有对象时,均遵循迭代器协议进行访问。
解释:
常有人疑惑,既然for循环基于迭代器协议,那么其遍历的对象是否都是迭代器?确实如此。既然for循环能遍历字符串、列表、字典、集合及文件对象,这些类型显然都是可迭代对象。然而,若直接定义列表l=[1,2,3,4],它本身并没有next()方法。
实际上,字符串、列表、元组、字典、集合和文件对象本身并非迭代器,但在for循环执行时,会调用它们内部的__iter__方法,将其转换为可迭代对象(即迭代器)。
随后,for循环调用该可迭代对象的__next__方法逐个取值,并自动捕获StopIteration异常,从而安全地终止迭代过程。
迭代器访问示例代码
l=[1,2,3,4,5]
#下标访问方式
print(l[0])
print(l[7]) #超出访问会报IndexError: list index out of range
#遵循迭代器协议的方式
diedai=l.__iter__()
print(diedai.__next__())
print(diedai.__next__())
print(diedai.__next__())
print(diedai.__next__())
print(diedai.__next__())
print(diedai.__next__()) #超出边界会报StopIteration
#for循环访问方式:
#for循环本质就是遵循迭代器协议的访问方式,先调用diedai.__iter__()方法,或者直接diedai=iter(l),然后依次执行diedai.next(),直到for循环捕捉到StopIteration终止循环
#for循环所有对象的本质都是一样的道理
for i in l: #diedai=l.__iter__()
print(l[i]) #i=diedai.next()
#使用while模拟for循环做的事情
diedai_l=l.__iter__()
while True:
try:
print(diedai_l.__next__())
except StopIteration:
print("迭代完毕,终止循环")
break
生成器基础概念
四、生成器初探
什么是生成器?
生成器可视为一种特殊的数据类型,它自动实现了迭代器协议(其他数据类型需手动调用__iter__方法),因此生成器本身即为可迭代对象。
生成器的分类及在Python中的表现形式:Python提供两种生成器创建方式。
1、生成器函数:采用常规函数定义,但使用yield语句而非return语句返回结果。yield语句每次仅返回一个结果,并在该结果与下一次执行之间挂起函数状态,以便下次从断点处继续执行。
2、生成器表达式:类似于列表推导式,但生成器返回按需产生的单个对象,而非一次性构建整个结果列表。
为何使用生成器以及生成器的优点:
Python利用生成器支持延迟操作(Lazy Evaluation),即仅在需要时才生成结果,而非立即生成。这是生成器的重要优势。
生成器函数与消费者模型示例
import time
# def producer():
# ret=[]
# for i in range(100):
# time.sleep(0.1)
# ret.append('包子%s' %i)
# return ret
#
# def consumer(res):
# for index,baozi in enumerate(res):
# time.sleep(0.1)
# print('第%s个人,吃了%s' %(index,baozi))
#
# res=producer()
# consumer(res)
#yield 3相当于return 控制的是函数的返回值
#x=yield的另外一个特性,接受send传过来的值,赋值给x
# def test():
# print('开始啦')
# firt=yield #return 1 first=None
# print('第一次',firt)
# yield 2
# print('第二次')
#
# t=test()
# res=t.__next__() #next(t)
# print(res)
# # t.__next__()
# # res=t.send(None)
# res=t.send('函数停留在first那个位置,我就是给first赋值的')
# print(res)
# def producer():
# ret=[]
# for i in range(100):
# time.sleep(0.1)
# ret.append('包子%s' %i)
# return ret
def consumer(name):
print('我是[%s],我准备开始吃包子了' %name)
while True:
baozi=yield
time.sleep(1)
print('%s 很开心的把【%s】吃掉了' %(name,baozi))
def producer():
c1=consumer('wupeiqi')
c2=consumer('yuanhao_SB')
c1.__next__()
c2.__next__()
for i in range(10):
time.sleep(1)
c1.send('包子 %s' %i)
c2.send('包子 %s' %i)
producer()
生成器小结
1、生成器是可迭代对象。
2、实现了延迟计算,节省内存空间。
3、生成器本质与其他数据类型一样,均实现了迭代器协议。区别在于,生成器附加了延迟计算和节省内存的特性,而其他可迭代对象不具备这一优势。
生成器表达式与列表推导对比
五、生成器表达式和列表解析
生成器表达式与列表解析代码示例
#1、三元表达式
name="alex"
name="yangyl"
res="1" if name=="yangyl" else "2"
print(res)
egg_list=["鸡蛋%s" %i for i in range(10) ] #列表解析
print(egg_list)
#使用生产器获取
egg_two=("鸡蛋%s" %i for i in range(10)) #生产器表达式
print(egg_two)
print(egg_two.__next__())
print(next(egg_two)) #next()本质就是调用__next__
总结:
1、将列表解析中的[]替换为(),即可得到生成器表达式。
2、列表解析与生成器表达式均为便捷的编程方式,但生成器表达式更节省内存。
3、Python不仅利用迭代器协议使for循环更加通用,大部分内置函数也使用迭代器协议访问对象。例如:sum函数使用迭代器协议访问对象,而生成器实现了迭代器协议,因此可以直接计算一系列值的和:
s1=sum(x ** 2 for x in range(4)) print(s1)
而无需先构造一个完整的列表。
s2=sum([x ** 2 for x in range(4)]) print(s2)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













