Python迭代器协议及for循环工作机制详解


Posted in Python onJuly 14, 2020

一、递归与迭代

二、什么是迭代器协议

1、迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个stopiteration异常,已终止迭代(只能往后走不能往前退)

2、可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义一个__iter__()方法)

3、协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。

三、python中强大的for循环机制

for循环的本质:循环所有对象,全部是使用迭代器协议

解释:

有时会想,for循环的本质就是遵循迭代器协议访问对象,那么for循环的对象肯定都是迭代器了啊,没错,那既然这样,for循环可以遍历(字符串,,列表,字典,集合,文件对象),那这些类型的数据肯定都是可迭代对象啊?但是,为什么定义一个列表l=[1,2,3,4]没有next()方法。

(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,只不过在for循环中,调用了他们内部的__iter__方法,把他们变成了可迭代对象

然后for循环调用可迭代对象的__next__方法去取值,而且for循环会捕捉stoplteration异常,已终止迭代

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使用生成器对延迟操作提供了支持,所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果,这也是生产器的重要好处

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函数是python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以我们可以直接这样计算一系列值的和:

s1=sum(x ** 2 for x in range(4))
print(s1)

而不用多此一举先构造一个列表

s2=sum([x ** 2 for x in range(4)])
print(s2)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
浅谈python for循环的巧妙运用(迭代、列表生成式)
Sep 26 Python
详解Python中的正则表达式
Jul 08 Python
Python3获取电脑IP、主机名、Mac地址的方法示例
Apr 11 Python
Python OS模块实例详解
Apr 15 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
May 18 Python
使用python爬取微博数据打造一颗“心”
Jun 28 Python
python格式化输出保留2位小数的实现方法
Jul 02 Python
简单了解python的break、continue、pass
Jul 08 Python
如何基于Python实现电子邮件的发送
Dec 16 Python
python3正则模块re的使用方法详解
Feb 11 Python
pyinstaller打包成无控制台程序时运行出错(与popen冲突的解决方法)
Apr 15 Python
对Keras自带Loss Function的深入研究
May 25 Python
windows10在visual studio2019下配置使用openCV4.3.0
Jul 14 #Python
解决python pandas读取excel中多个不同sheet表格存在的问题
Jul 14 #Python
Python matplotlib读取excel数据并用for循环画多个子图subplot操作
Jul 14 #Python
python3 循环读取excel文件并写入json操作
Jul 14 #Python
Python爬虫实例——scrapy框架爬取拉勾网招聘信息
Jul 14 #Python
Python爬虫爬取新闻资讯案例详解
Jul 14 #Python
Win10下配置tensorflow-gpu的详细教程(无VS2015/2017)
Jul 14 #Python
You might like
php中去除所有js,html,css代码
2010/10/12 PHP
关于IIS php调用com组件的权限问题
2012/01/11 PHP
详解PHP的Yii框架中自带的前端资源包的使用
2016/03/31 PHP
PHP实现的网站目录扫描索引工具
2016/09/08 PHP
php实现和c#一致的DES加密解密实例
2017/07/24 PHP
php实现微信模板消息推送
2018/03/30 PHP
php面试中关于面向对象的相关问题
2019/02/13 PHP
Thinkphp集成抖音SDK的实现方法
2020/04/28 PHP
js 父窗口控制子窗口的行为-打开,关闭,重定位,回复
2010/04/20 Javascript
给超链接添加特效鼠标移动展示提示信息且随鼠标移动
2013/10/17 Javascript
js实现带缓冲效果的仿QQ面板折叠菜单代码
2015/09/06 Javascript
基于jQuery实现咖啡订单管理简单应用
2017/02/10 Javascript
vue上传图片组件编写代码
2017/07/26 Javascript
angular 内存溢出的问题解决
2018/07/12 Javascript
详解Vue.js使用Swiper.js在iOS
2018/09/10 Javascript
vue项目打包后上传至GitHub并实现github-pages的预览
2019/05/06 Javascript
Vue项目前后端联调(使用proxyTable实现跨域方式)
2020/07/18 Javascript
[01:05:00]2018国际邀请赛 表演赛 Pain vs OpenAI
2018/08/24 DOTA
[52:06]完美世界DOTA2联赛决赛日 Inki vs LBZS 第一场 11.08
2020/11/10 DOTA
Python最基本的数据类型以及对元组的介绍
2015/04/14 Python
Python编程中装饰器的使用示例解析
2016/06/20 Python
Python获取当前路径实现代码
2017/05/08 Python
python3 pillow生成简单验证码图片的示例
2017/09/19 Python
对python3中, print横向输出的方法详解
2019/01/28 Python
Python如何向SQLServer存储二进制图片
2020/06/08 Python
基于python实现matlab filter函数过程详解
2020/06/08 Python
Python环境管理virtualenv&virtualenvwrapper的配置详解
2020/07/01 Python
Lentiamo比利时:便宜的隐形眼镜
2020/02/14 全球购物
介绍下WebSphere的安全性
2013/01/31 面试题
工作的心得体会
2013/12/31 职场文书
学校三节实施方案
2014/06/09 职场文书
2015年大学生实习评语
2015/03/25 职场文书
解决MultipartFile.transferTo(dest) 报FileNotFoundExcep的问题
2021/07/01 Java/Android
SpringBoot集成Redis的思路详解
2021/10/16 Redis
关于MybatisPlus配置双数据库驱动连接数据库问题
2022/01/22 Java/Android
BCL经典机 SONY ICF-5900W电路分析
2022/04/24 无线电