如何快速理解Python中的for循环?

news2024/12/29 10:57:22

人生苦短,我用python

这次来给大家带来一点干货,

我们将从一组基本例子和它的语法开始,
还将讨论与 for 循环关联的 else 代码块的用处。

然后我们将介绍迭代对象、迭代器和迭代器协议,
还会学习如何创建自己的迭代对象和迭代器。

之后,我们将讨论如何使用迭代对象和迭代器实现 for 循环,
以及利用 while 循环通过迭代器协议实现 for 循环逻辑。

最后,我们将反编译一个简单的 for 循环,
并逐步介绍 Python 解释器在执行 for 循环时执行的指令,
以满足大家的好奇心。
这些有助于理解 for 循环运行时的内部工作原理。


Python的for循环

for 语句是 Python 中执行迭代的两个语句之一,
另一个语句是 while。
如果你对 Python 的迭代并不是很熟悉的话,
Python中的迭代:for、while、break、以及continue语句是一个不错的切入点。

Python 中,for 循环用于遍历一个迭代对象的所有元素。
循环内的语句段会针对迭代对象的每一个元素项目都执行一次。
暂且可以将迭代对象想象成一个对象集合,我们可以一个个遍历里面的元素。
我们将在下一节对迭代器和迭代对象作详细说明。

一个简单的 for 循环

有什么python相关报错解答自己不会的、或者源码资料/模块安装/女装大佬精通技巧 都可以来这里:(https://jq.qq.com/?_wv=1027&k=PtfPV48p)或者+V:python10010问我

我们先从一个简单 for 循环开始,
它遍历一个字符串列表并打印每一个字符串。

在这里插入图片描述

如你所见,
这个循环实际上遍历了列表中的每一个单词并打印它们。
也就是说,
在循环的每一次遍历中,
变量 word 都被指定为列表中的一个元素,
然后执行 for 语句中的代码块。
由于列表是一个有序的元素序列,
所以循环也是以相同的顺序遍历这些元素。

带有 else 子句的 for 循环

Python 中的 for 循环可以选择是否关联一个 else 子句。
else 子句中的代码块是在 for 循环完成后才开始执行的,
即在迭代对象中的所有元素都遍历完毕之后。
现在我们看一下如何扩展前面的示例以包含一个 else 条件(子句)。

在这里插入图片描述

else 子句适用于何时?

你已经注意到,
else 子句是在 for 循环完成之后才执行的。
那么 else 代码块的意义是什么呢?
for 循环之后的语句不是也是同样会执行吗?

我们很多时候会遇到这样一种情况,
当满足某种条件时,中途结束 for 循环。
且如果这个条件一直未满足,则希望执行另一组语句。
我们通常使用布尔类型的标记实现,下面是一个例子。

在这里插入图片描述

调用结果:

在这里插入图片描述
而用 else 代码块的话,

我们可以避免使用布尔类型的标记found_item。

我们看看如何使用 else 子句重写上面的方法。

注意如果 for 循环中的 break 语句被触发执行,

那么则会跳过 else 块。

在这里插入图片描述
所以 else 代码块适用于 for 循环中有 break 语句的情况,且我们希望 break 条件没有被触发的时候执行一些语句。

否则,与 else 关联的语句只会在 for 循环结束时才执行。本文的最后一节查看反编译的字节码时你会看到这一点。

for 循环语法

我们已经看到了一些简单的例子,

接下来以 for 循环的语法结束本节。

在这里插入图片描述基本上,对于 iterable 中的每一个元素,
都会执行 set_of_statements_1。
一旦所有的元素都迭代一遍,
控制器将跳转到 else 代码块中执行 set_of_statements_2。

注意,else 子句是可选的。
如果没有发现 else 子句,
循环会在所有元素都遍历完成后结束,
并且控制器会转向程序之后的语句。


可迭代对象与迭代器

可迭代对象

在上一节,
我们使用术语 iterable 来表示循环中被迭代的对象。
现在我们来试着了解一下 Python 中的 iterable 对象是什么。

Python 中,
一个 iterable 对象指在 for 循环中可以被迭代的任意对象。
这意味着,
当这个对象作为参数传递给 iter()方法时应该返回一个迭代器。
我们来看一下 Python 中的一些常用的内置迭代的例子。

在这里插入图片描述

如你所见,
当我们对一个 iterable 对象调用 iter() 时,
它会返回一个迭代器对象。

迭代器

那么什么是迭代器呢?
迭代器在 Python 中被定义为一个表现为流式数据的对象。
基本上,如果我们将对象传递给内置的next() 方法,
它应该从与之关联的流式数据中返回下一个值。
一旦所有的元素都遍历结束,
它会抛出一个StopIteration 异常。
next()方法的后续调用也都会抛出StopIteration 异常。

我们用一个列表来试一下。

在这里插入图片描述
迭代器也是可迭代对象!但是…

有一个很有趣的事需要记一下,
迭代器同样支持(强制要求支持迭代器协议)iter() 方法。

这意味着我们可以对一个迭代器调用iter() 方法并获取它自身的迭代器对象。

在这里插入图片描述
因此,我们可以在任何期望使用迭代器的地方使用它。
比如,for 循环。

然而要注意一点,
在像 list 这样的容器对象上调用 iter() 每次都会返回不同的迭代器,
而在迭代器上调用 iter() 仅仅返回同一个迭代器。

在这里插入图片描述在这里插入图片描述
所以如果你需要进行多次迭代,
并且用迭代器替换普通容器或可迭代对象,
那么第二次你会看到一个空的容器。

对一个列表迭代两次

请注意,这是按照我们的期望运行的。

在这里插入图片描述对一个列表迭代器迭代两次

请注意,迭代器在第一次循环的时候就已经结束了,
第二次我们看到的是一个空容器。

在这里插入图片描述

迭代器协议

有什么python相关报错解答自己不会的、或者源码资料/模块安装/女装大佬精通技巧 都可以来这里:(https://jq.qq.com/?_wv=1027&k=PtfPV48p)或者+V:python10010问我

前文我们看到了:

1. 一个可迭代对象,作为参数传递给 iter() 方法时返回一个迭代器。

2. 一个迭代器,

  1. 作为参数传递给next()方法时返回它的下一个元素或者在所有元素都遍历结束时抛 出StopIteration 异常。

  2. 作为参数传递给iter() 方法时返回它自身。

迭代协议仅仅只是一种将对象定义为迭代器的标准方式。我们已经在前一节看到了这种协议的实际应用。根据协议,迭代器应该定义以下两个方法:

  1. next()

    1. 每次调用这个方法时,应该返回迭代器的下一个元素。一旦元素都遍历结束,它应该抛出StopIteration 异常。
  2. 当我们调动内置函数next() 时,实际内部调用的是本方法。

  3. iter()

    1. 这个方法返回迭代器自身
  4. 当我们调动内置函数iter() 时,实际内部调用的是本方法。

自己写一个迭代器

现在我们已经知道迭代协议的原理,
可以写一个自己的迭代器了。
我们先看一个例子,
下面我们创建了一个根据给定范围和步长的 Range 类。

在这里插入图片描述
我们看一下它在 for 循环中是怎么工作的。

在这里插入图片描述
注意,Range 类的实例是迭代器也是可迭代对象。

自己写一个可迭代对象

我们还可以基于 Range 迭代器另外创建一个可迭代对象。
它的作用是每当调用 iter() 方法是返回一个新的迭代器,
在这里,
它应该返回一个新的 Range 对象。

在这里插入图片描述
在 for 循环中使用我们这个 RangeIterable。

在这里插入图片描述
for 循环工作原理

现在我们已经知道什么是迭代器和可迭代对象,接下来了解一下 for 循环是如何工作的。

再看一下前面的例子。

当我们执行上面的代码块时,发生了以下这些事情:

在这里插入图片描述

  1. 在 for 语句内部对列表 [“You”, “are”, “awesome!”] 调用了 iter() 方法,返回结果是一个迭代器。

  2. 然后对迭代器调用 next() 方法,并将其返回值赋给变量 word。

  3. 之后,会执行 for 循环中关联的语句块。这个例子中是打印 word。

  4. 在 next() 方法抛出 StopIteration 之前会一直重复执行第 2,3 步。

  5. 一旦 next() 抛出 StopIteration,控制器会跳转到 else 子句(如果存在)并执行与 else 关联的语句块。

**注意:**如果在步骤 3 中,for 循环语句遇到了 break 语句,则跳过 else 代码块。

使用 while 语句实现 for 循环逻辑

我们可以像下面这样使用 while 语句实现之前的逻辑。

while 循环的行为实际上与 for 循环相同,

上面的代码会有以下输出。

在这里插入图片描述

在这里插入图片描述

反编译 for 循环

在本节,
我们将反编译 for 循环并逐步说明解释器在执行 for 循环时的指令。
这里使用dis 模块来反编译 for 循环。
详细来说,
就是我们将使用 dis.dis 方法来生成可读性更高的字节码。

我们会使用之前一直用的简单 for 循环示例。
接下来将文件写入文件 for_loop.py。

在这里插入图片描述
我们可以调用 dis.dis 方法获得可读性高的字节码。
在终端上运行以下命令。

在这里插入图片描述
反编译输出的每列表示以下内容:

  1. 第 1 列:代码行数。

  2. 第 2 列:如果是跳转指令,则有 “>>” 符号。

  3. 第 3 列:以字节为单位的字节码偏移量。

  4. 第 4 列:字节码指令本身。

  5. 第 5 列:展示指令的参数。如果括号中有内容,它只是对参数做了更好的可读性转化。

现在我们来一步步浏览反编译后的字节码,并尝试了解实际发生了什么。

1. 第 1 行,即,“for word in [“You”, “are”, “awesome!”]:” 转译为:

0 SETUP_LOOP 28 (to 30)

该语句将 for 循环中的代码块推送到栈中。这段代码块会跨越 28 个字节,达到 “30”。

这意味着,如果 for 循环中有 break 语句,那么控制器将跳转到偏移位置 “30”。注意当遇到 break 语句时是如何跳过 else 代码块的。

2 LOAD_CONST 0 ((‘You’, ‘are’, ‘awesome!’))

接下来,列表被推送到栈顶(TOS,之后使用 TOS 表示栈顶或栈顶元素)。

4 GET_ITER

该指令实现 “TOS = iter(TOS)”。这表示从列表获取一个迭代器(当前为 TOS),然后将迭代器推送给 TOS。

6 FOR_ITER 12 (to 20)

该指令获取 TOS,作为当前的迭代器, 并调用 next() 方法。

如果 next() 方法产生一个值,则将其作为 TOS 推送到栈,并执行吓一跳指令 “8 STORE_NAME”。

一旦 next() 表明迭代器已经遍历结束(即抛出 StopIteration 异常),TOS(迭代器)将从栈中弹出,字节码计数器会增加 12。这表示控制器跳转到指令 “20 POP_BLOCK”。

8 STORE_NAME 0 (word)

这个指令执行了转换 word = TOS,即,next()返回的值被赋给变量word。

2. 第 1 行,即,“print(word)” 转译为:

10 LOAD_NAME 1 (print)

将可调用方法print 推送到栈中。

12 LOAD_NAME 0 (word)

将栈中的word作为参数推送给print。

14 CALL_FUNCTION 1

调用带位置参数的函数。

像我们看到的指令那样,与函数关联的参数会出现在 TOS 中。在获得可调用象的对(如print)之前,会弹出所有遇到的参数。

一旦获得可调用对象,则把所有参数传递给它并调用。

可调用对象执行结束后,把返回值推送到 TOS 中,这里是 None。

16 POP_TOP

TOS(栈顶元素),即将函数的返回值从栈中移除(弹出)。

18 JUMP_ABSOLUTE 6

此时字节码计数器为 “6”,这表示下一条指令将执行 “6 FOR_ITER”。这是循环遍历迭代器中元素的方式。

注意,一旦迭代器中的元素都遍历结束,指令 “6 FOR_ITER” 会结束循环并跳转到 “20 POP_BLOCK”。

20 POP_BLOCK

POP_BLOCK 会从代码块的栈中移除由 “0 SETUP_LOOP” 设置的代码块。

3. 注意第 3 行(对应else),没有关联任何特殊指令。程序控制器会顺序执行下一条与else 相关的指令。

4. 第 4 行,即,“print(“See you later!”)” 转译为:

22 LOAD_NAME 1 (print)

推送与print 相关的可调用方法到栈中。

24 LOAD_CONST 1 (‘See you later!’)

推送可调用函数的参数对象到栈中。

26 CALL_FUNCTION 1

可调用函数及其参数会从栈中弹出,然后执行函数并将其返回值推送到 TOS。

28 POP_TOP

TOS(栈顶元素),即将函数返回值(这里是 None)从栈中移除。

5. 下面的两个指令只是简单的将脚本的返回值(None)加载到栈并返回。

30 LOAD_CONST 2 (None)

32 RETURN_VALUE

喔!现在我们已经了解了 for 循环反编译后的指令。希望这有助于更好地理解 for 循环的工作原理。

有什么python相关报错解答自己不会的、或者源码资料/模块安装/女装大佬精通技巧 都可以来这里:(https://jq.qq.com/?_wv=1027&k=PtfPV48p)或者+V:python10010问我

今天的文章就是这样啦

只有学习才能使你立于不败之地

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/122276.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序云开发之用户输入数据后excel表格导出升级版

大家好,我是csdn的小博主lqj_本人,最近在哔哩哔哩开始上传我的制作微信小程序的详细流程,大家可以关注一下哔哩哔哩:小淼前端 本次程序的详细视频教程已上传至哔哩哔哩: 腾讯云开发小程序之用户输入数据excel自动导出系…

HaaS EDU物联网项目实战:微信小程序实现云养花

HaaS EDU K1是一款高颜值、高性能、高集成度的物联网开发板,板载功能强大的4核(双核300Mhz M33双核1GHz A7)主芯片,2.4G/5G双频Wi-Fi,双模蓝牙(经典蓝牙/BLE),并自带丰富的传感器与小…

第一章 vscode安装java环境

要在Visual Studio Code中配置Java环境,需要完成以下步骤: 安装Java Development Kit (JDK)。首先,你需要安装Java Development Kit (JDK),这是Java的开发环境,包含了Java虚拟机、Java编译器和Java库等。可以前往Oracl…

Python基础知识入门(五)

Python基础知识入门(一) Python基础知识入门(二) Python基础知识入门(三) Python基础知识入门(四) 一、模块应用 模块是一个包含所有定义的函数和变量的文件,其后缀名…

2022年「博客之星」参赛博主:(天寒雨落)在等您评价 ~

目录 评价方法 参与规则 评选规则 评分规则 活动奖品 评价方法 点击链接:2022年「博客之星」参赛博主:天寒雨落-CSDN社区 在箭头所指位置做出打星评价。 参与规则 1.本次年度评选分为「博客之星|和「博客新星:以及「社区之星|。「博客新星:只针对…

Kafka — 1、基础介绍

1、消息队列简介 (1)同步:多个服务之间是同步完成一次请求 缺点: a. 性能比较差 b. 稳定性比较差,如果其中一个服务没有执行成功,则整个请求执行失败 (2)异步:加入【消息…

自动控制原理笔记-线性系统的稳态误差

目录 1.误差与稳态误差 2.计算稳态误差的一般方法 3.静态误差系数法 例题: 稳态误差是系统的稳态性能指标,是系统控制精度的度量。 这里讨论的只是系统的原理性误差,不包括非线性等因素所造成的附加误差。 计算系统的稳态误差以系统稳定…

洛谷千题详解 | P1029 [NOIP2001 普及组] 最大公约数和最小公倍数问题【C++语言】

博主主页:Yu仙笙 专栏地址:洛谷千题详解 目录 题目描述 输入格式 输出格式 输入输出样例 解析: C源码: C源码2: C源码3: ------------------------------------------------------------------------------…

2022博客之星年度总评选开始了

作者简介:陶然同学 专注于Java领域开发 熟练掌握Java、js等语言的“Hello World” CSDN原力计划作者、CSDN内容合伙人、Java领域优质作者、Java领域新星作者、51CTO专家、华为云专家、阿里云专家等 🎬 陶然同学🎥 由 陶然同学 原创&#…

Linux之SQL Server数据库安装

一、SQL Server简介 SQL Server 是一个关系数据库管理系统。它最初是由Microsoft Sybase 和Ashton-Tate三家公司共同开发的,于1988 年推出了第一个OS/2 版本。在Windows NT 推出后,Microsoft与Sybase 在SQL Server 的开发上就分道扬镳了,Micr…

密码学 公开密钥管理

PKU概念 Public Key Infrastructure PKI一般指公钥基础设施。 公钥基础设施是一个包括硬件、软件、人员、策略和规程的集合,用来实现基于公钥密码体制的密钥和证书的产生、管理、存储、分发和撤销等功能。 基于PKI的信任模型 如果一个个体假设CA 能够建立并维持一…

ASP.NET Core 3.1系列(21)——EFCore中的更新实体操作

1、前言 前面的博客已经介绍过EFCore中关于新增和删除实体的相关操作,本文开始介绍EFCore中的更新实体操作。与新增实体和删除实体相比,更新实体的操作略微有些复杂,如果在代码的写法上不多加注意,那就很有可能会在后台生成效率低…

利用空余时间成为“业余”的自动驾驶的开发者

作为一名开发者,我时常会阅读一些相关的技术杂志和周刊,了解一些近期比较热门的技术和事件,要说现在技术领域最有发展前景的方向之一,很多人会想到自动驾驶。但现在国内做自动驾驶平台的并不多,其中百度做得是相对比较…

【Web】浅谈Http的请求方式和数据请求格式ContentType

我本来Http的请求方式和数据请求格式是大家开发过程中都默认知道的事情,直到我发现我的前端竟然不知道表单请求的时候,我觉得我有必要跟大家一起来讨论一下这个话题了。有可能我的前端小伙伴在学习的时候一开始就入手现在比较流行的前端框架如Vue、React…

3dmax 建模插件 Rappa Tools 3 笔记

1功能概述: RappaTools3是一个高级工具箱,为在3ds Max中工作的艺术家提供了各种各样的工具。主要的重点是加快工作流程和减少点击量。它提供了各种各样的工具,从选择工具到渲染工具。它可以帮助您完成创建3D艺术作品的整个过程。 它带有3个…

C#,图像二值化(05)——全局阈值的联高自适应算法及其源代码

阈值的选择当然希望智能、简单一些。应该能应付一般的图片。 What is Binarization? Binarization is the process of transforming data features of any entity into vectors of binary numbers to make classifier algorithms more efficient. In a simple example, trans…

优思学院|怎么把DPMO/不良率换算成六西格玛水平?

如何计算西格玛水平? 为了更形像化地说明西格玛水平(Sigma Level),我们设定一个场景作为例子,假设你是一家电力公司,你会如何评估你公司的质量水平呢?你可能会以电网供电时的正常运行时间来衡量…

知识付费海哥:知识变现三剑客

小时候看武侠戏,那些古代剑客,飞檐走壁,神出鬼没,行走江湖,行侠仗义,牛逼的很!心里贼仰慕! 记忆最深的,是剑客们稳、准、狠的绝技剑法,剑起头落,一…

python实现字幕雨效果实现

先看最终实现的效果图: 使用python实现以上字幕雨效果,用到的主要库是pygame; pygame不是内置模块,需要先安装一下: 安装pygame 安装方式推荐有很多种,推荐使用pip; pip 是 Python 的包安装程…

RabbitMQ 订阅模型-路由模式

订阅模型-路由模式,此时生产者发送消息时需要指定 RoutingKey,即路由 Key,Exchange 接收到消息时转发到与 RoutingKey 相匹配的队列中。 在 Direct 模型下: 队列与交换机绑定,不能任意绑定,而要指定一个 Ro…