详解Python中for循环是如何工作的


Posted in Python onJune 30, 2017

前言

for...in 是Python程序员使用最多的语句,for 循环用于迭代容器对象中的元素,这些对象可以是列表、元组、字典、集合、文件,甚至可以是自定义类或者函数,例如:

作用于列表

>>> for elem in [1,2,3]:
...  print(elem)
...
1
2
3

作用于元组

>>> for i in ("zhang", "san", 30):
...  print(i)
...
zhang
san
30

作用于字符串

>>> for c in "abc":
...  print(c)
...
a
b
c

作用于集合

>>> for i in {"a","b","c"}:
...  print(i)
...
b
a
c

作用于字典

>>> for k in {"age":10, "name":"wang"}:
...  print(k)
...
age
name

作用于文件

>>> for line in open("requirement.txt"):
...  print(line, end="")
...
Fabric==1.12.0
Markdown==2.6.7

可能有人不经要问,为什么这么多不同类型对象都支持 for 语句,还有哪些类型的对象可以作用在 for 语句中呢?回答这个问题之前,我们先要了解 for 循环背后的执行原理。

for 循环是对容器进行迭代的过程,什么是迭代?迭代就是从某个容器对象中逐个地读取元素,直到容器中没有更多元素为止。那么,哪些对象支持迭代操作?任何对象都可以吗?先随便自定义一个类试试,看行不行:

>>> class MyRange:
...  def __init__(self, num):
...   self.num = num
...
>>> for i in MyRange(10):
...  print(i)
...
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: 'MyRange' object is not iterable

错误堆栈日志非常清楚地告诉我们,MyRange 不是一个可迭代对象,所以它不能用于迭代,那么到底什么样的对象才称得上是可迭代对象(iterable)呢?

可迭代对象需要实现__iter__方法,并返回一个迭代器,什么是迭代器呢?迭代器只需要实现 __next__方法。现在我们就来验证一下列表为什么支持迭代:

>>> x = [1,2,3]
>>> its = x.__iter__() # x有此方法,说明列表是可迭代对象
>>> its
<list_iterator object at 0x100f32198>

>>> its.__next__() # its有此方法,说明its是迭代器
1
>>> its.__next__()
2
>>> its.__next__()
3
>>> its.__next__()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

从试验结果来看,列表是一个可迭代对象,因为它实现了 __iter__方法,并且返回了一个迭代器对象(list_iterator),因为它实现了 __next__方法。我们看到它不断地调用__next__方法,其实就是不断地迭代获取容器中的元素,直到容器中没有更多元素抛出 StopIteration 异常为止。

那么 for 语句又是如何循环的呢?到这里,恐怕你也猜到了,它的步骤是:

  • 先判断对象是否为可迭代对象,不是的话直接报错,抛出TypeError异常,是的话,调用 __iter__方法,返回一个迭代器
  • 不断地调用迭代器的__next__方法,每次按序返回迭代器中的一个值
  • 迭代到最后,没有更多元素了,就抛出异常 StopIteration,这个异常 python 自己会处理,不会暴露给开发者

详解Python中for循环是如何工作的

对于元组,字典,字符串也是同样的道理,弄明白了 for 的执行原理之后,我们就可以实现自己的迭代器用在 for 循环中。

前面的 MyRange 报错是因为它没有实现迭代器协议里面的这两个方法,现在继续改进:

class MyRange:
 def __init__(self, num):
  self.i = 0
  self.num = num

 def __iter__(self):
  return self

 def __next__(self):
  if self.i < self.num:
   i = self.i
   self.i += 1
   return i
  else:
   # 达到某个条件时必须抛出此异常,否则会无止境地迭代下去
   raise StopIteration()

因为它实现了__next__方法,所以 MyRange 本身已经是一个迭代器了,所以 __iter__返回的就是对象本身 self。现在用在 for 循环中试试:

for i in MyRange(3):
 print(i)
# 输出
 0
 1
 2

有没有发现,自定义的 MyRange 功能和内建函数 range很相似。for 循环本质是不断地调用迭代器的__next__方法,直到有 StopIteration 异常为止,所以任何可迭代对象都可以作用在for循环中。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
使用python Django做网页
Nov 04 Python
python插入排序算法的实现代码
Nov 21 Python
Python模拟浏览器上传文件脚本的方法(Multipart/form-data格式)
Oct 22 Python
Python常用爬虫代码总结方便查询
Feb 25 Python
python pandas cumsum求累计次数的用法
Jul 29 Python
Python 获取命令行参数内容及参数个数的实例
Dec 20 Python
python logging通过json文件配置的步骤
Apr 27 Python
基于plt.title无法显示中文的快速解决
May 16 Python
5行Python代码实现图像分割的步骤详解
May 25 Python
python识别验证码的思路及解决方案
Sep 13 Python
详解用 python-docx 创建浮动图片
Jan 24 Python
详解Go语言运用广度优先搜索走迷宫
Jun 23 Python
python 连接sqlite及简单操作
Jun 30 #Python
利用Python破解斗地主残局详解
Jun 30 #Python
Python实现的文本编辑器功能示例
Jun 30 #Python
Python构建XML树结构的方法示例
Jun 30 #Python
基于python的Tkinter编写登陆注册界面
Jun 30 #Python
Python使用微信SDK实现的微信支付功能示例
Jun 30 #Python
python实现的二叉树定义与遍历算法实例
Jun 30 #Python
You might like
使用session判断用户登录用户权限(超简单)
2013/06/08 PHP
php计算几分钟前、几小时前、几天前的几个函数、类分享
2014/04/09 PHP
PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】
2017/11/16 PHP
php函数式编程简单示例
2019/08/08 PHP
php实现通过stomp协议连接ActiveMQ操作示例
2020/02/23 PHP
PHP 实现重载
2021/03/09 PHP
JavaScript入门教程(12) js对象化编程
2009/01/31 Javascript
Jquery 点击按钮显示和隐藏层的代码
2011/07/25 Javascript
IE6、IE7中setAttribute不支持class/for/rowspan/colspan等属性
2011/08/28 Javascript
JS清空多文本框、文本域示例代码
2014/02/24 Javascript
jQuery实现网页顶部固定导航效果代码
2015/12/24 Javascript
jQuery UI库中dialog对话框功能使用全解析
2016/04/23 Javascript
jQuery和hwSlider实现内容响应式可触控滑动切换效果附源码下载(二)
2016/06/22 Javascript
JS打印组合功能
2016/08/04 Javascript
浅析 NodeJs 的几种文件路径
2017/06/07 NodeJs
详解Angular4中路由Router类的跳转navigate
2017/06/09 Javascript
微信小程序实现点击按钮修改文字大小功能【附demo源码下载】
2017/12/06 Javascript
Vue多种方法实现表头和首列固定的示例代码
2018/02/02 Javascript
Vue-cropper 图片裁剪的基本原理及思路讲解
2018/04/17 Javascript
ES6基础之默认参数值
2019/02/21 Javascript
NodeJS读取分析Nginx错误日志的方法
2019/05/14 NodeJs
vue中通过使用$attrs实现组件之间的数据传递功能
2019/09/01 Javascript
layer.open 获取不到表单信息的解决方法
2019/09/26 Javascript
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
2020/02/07 Python
浅谈keras保存模型中的save()和save_weights()区别
2020/05/21 Python
Electrolux伊莱克斯巴西商店:家用电器、小家电和配件
2018/05/23 全球购物
Skyscanner台湾:全球知名的旅行比价引擎
2018/07/01 全球购物
美国打印机墨水和碳粉购物网站:QuikShip Toner
2018/08/29 全球购物
会计电算化学生个人的自我评价
2014/02/08 职场文书
销售内勤岗位职责
2015/02/10 职场文书
销售经理岗位职责范本
2015/04/02 职场文书
2015年秋季运动会前导词
2015/07/20 职场文书
2016年八一建军节活动总结
2016/04/05 职场文书
通知怎么写?
2019/04/17 职场文书
不要在HTML中滥用div
2021/05/08 HTML / CSS
Python中zipfile压缩包模块的使用
2021/05/14 Python