查看原文:【数据seminar】Python教学 | Python 中的循环结构(上)【附本文代码和数据】 (qq.com)
Part1引言
上期文章我们向大家介绍了 Python 程序控制结构中的分支结构,也就是实用性极高的判断语句。在介绍它时我们曾明言,分支结构经常与循环结构一起使用,这是因为分支结构只可以做一件事,而循环结构加上分支结构则可以把这件事在短时间内做千千万万遍。可以说只有掌握循环结构,才具备批量处理数据的能力。
我们在>>>上期文章【Python教学 | Python 中的分支结构(判断语句)】中使用一个判断家庭农场是否从事水果种植或销售的案例来介绍判断语句的功能,基本情况如下。
数据来源:浙大卡特-企研中国涉农研究数据库(CCAD)
在上图所示的全国 36 万多家家庭农场数据(截图为样例数据)中,通过判断其经营范围中是否包含与水果有关的词语(“水果”、“果蔬”、“梨”……)来做出判断。可是判断语句一次只能判断一个家庭农场是否包含相关词语,而我们要处理的是全部 36 万多条数据,所以在实际处理这个需求的时候,我们要把判断语句放到能够批量处理数据的循环结构中,对每一个家庭农场的经营范围都做处理,判断是否包含相关词汇,最终才得以完成对整张表的处理。
下面我们来介绍 Python 循环结构的具体内容。
Part2循环结构简介
从结构上来说,循环结构是在程序中需要反复执行某个功能而设置的一种程序结构。从代码层面来讲,循环是程序设计语言中反复执行某些代码的一种计算机处理过程,常见的有遍历循环(按次数循环)和无限循环(按条件循环),在 Python 中分别对应着 for 循环 和 while 循环。
下面我们通过两个场景简单认识一下这两种循环结构。
1遍历循环
逐一查看这些家庭农场是否拥有商标,如果拥有商标,那么就把这个家庭农场的信息另存入一张表中;如果未拥有商标,则跳过不做处理。
我们可以将上述场景转换成一个使用了for 循环 + 单分支结构的程序,那么下面这些对照会帮助大家理解这个 “程序”。
- 310465 家在营的家庭农场代表循环体
- “逐一” 代表 for 循环(的动作),即一个一个地处理循环体中所有元素
- “查看” 代表单分支结构中的判断条件,判断家庭农场是否拥有商标
- 存入一张表/还是跳过则是在面对不同的判断结果做出的不同反应
下图是该 “程序” 的示意图。黄色圆形代表循环体,其中包含 310465 家在营家庭农场的信息(包括是否拥有商标的标识字段);for 循环每次从循环体中获取一个家庭农场的相关信息并根据是否拥有商标做出不同的处理,处理过后继续获取循环体中下一个(注:for 循环会按照循环体中元素的顺序依次取出元素,而不是每次随机获取一个元素)家庭农场的信息并进行判断和处理,直到所有的家庭农场都经过处理,循环就会结束。
2无限循环
无限循环并不一定真的会无限制地执行循环,之所以称之为无限循环,是因为循环的次数是不确定的。只有满足终止循环的条件才会退出循环,用一句话概括——不达目的誓不罢休!
统一社会信用代码。但由于部分家庭农场在统一社会信用代码推出之前(2015 年 6 月 4日 正式推出)就已经注销,所以这部分家庭农场没有统一社会信用代码,如下图所示。
我们将表中的 “统一社会信用代码” 字段转为 列表 A,由于一些机构缺少统一社会信用代码,导致一些空值 ''也混入了列表 A,接下来我们要做的就是 将列表 A 中的所有空值 '' 全部删除,只保留有效的统一社会信用代码。可问题是列表类型每次只能删除一个元素,所以需要用到循环结构来解决问题, 处理思路是每次从列表 A 删除一个空值,直至列表 A 中不再含有空值。处理后的列表 A 可以写入数据表另作他用。
下图是使用无限循环(while 循环)解决上述问题的流程图。
通过以上场景,我们已经能够初步了解 Python 中两种循环结构的特点。顺便提一句,在数据处理和数据分析中,遍历循环的使用频率相对更高。
Part3遍历循环中的循环体
在 Python 中的两种循环结构中,只有遍历循环(for 循环)中有循环体的概念。我们从上一节的场景中可以大致了解到循环体是一种能够存放多个元素的类型,因为只有这样,循环才会有意义。那么哪些类型可以在 for 循环中充当循环体的角色呢?
在 Python 中,只要是可迭代的对象都可以作为循环体,由于 Python 中的数据类型不仅仅只有基础的数据类型,还包括其他第三方库中的类型,例如科学计算库 numpy 中的 array 类型、数据处理与分析库 pandas 中的表结构 DataFrame
# 从标准库 collections 导入 Iterable 方法
from collections import Iterable
# 使用 isinstance 方法 + Iterable 方法可以判断下面语句
# 中的 “Target” 是否为可迭代对象,如果是,会返回 True, 否则返回 False
print(isinstance(Target, Iterable))
下面我们使用上述方法验证 Python 中哪些基础数据类型是可迭代的,那些是不可迭代的。验证过程如下图所示。
经过验证,得知字符串以及四种组合数据类型都是可迭代的,可以作为循环体。即使是其中无序的集合类型,也可以在 for 循环中使用,只不过由于集合的无序性,循环中元素的顺序是随机的,循环的次数仍是集合中元素的数量。除了以上基础类型之外,range() 函数也是一种常用作循环体的对象(作为循环体时其中的元素均为整数),它最大的特点是可以指定循环的次数。比如在处理一个 10000 行的数据时,我们使用 range 函数指定循环次数为 10000 次,在循环过程中每次处理一行数据,最后刚好在处理完全部 10000 行数据后退出循环。
range()
range(start,stop[,step]) # [] 代表非必需,即可省略参数
上述代码是 range() 函数的标准用法,共有三个参数。其中参数 start 表示起始数字,默认值为 0;参数 stop 表示终止数字;参数 step 表示步长,默认值为 1。这三个参数中,只有 stop 参数是必不能省略的。下面我们使用代码来分情况介绍 range() 函数的具体用法。
(1) 不省略任何参数
(2)省略参数step
(3)只使用参数stop
Part4循环结构的 Python 代码
1遍历循环——for 循环语句
Python 中 for 循环(或者说 for-in 循环)的功能是依次取出循环体中的元素,并根据这些元素处理相关的数据。循环体中所有元素都经过处理后,循环自动结束。for 循环语法格式和示意图如下。
for <循环变量> in <循环体>:
<语句块>
下面我们使用一个实例来介绍一下 for 循环。
【场景1】在全国所有在市场监管部门登记在册的家庭农场数据中,统计出成立时间在 2018-2021年之间的家庭农场数量。
【解决方式】先将家庭农场成立时间这一字段转为列表,随后循环此列表,每发现一个成立日期在指定时间区间内的数据,则目标统计值加1,代码如下。
# 将表中成立时间这一字段转为列表,那么列表中的数据均是企业成立时间
ESDATE = list(data['成立时间'])
# 初始化目标统计值
Num = 0#
使用 for 循环遍历列表 ESDATEfor date in ESDATE:
# date 就是列表 ESDATE 中的元素,也就是一个成立日期
# 设置判断条件判断是都是符合条件的日期
if 2018 <= date.year <= 2021:
Num += 1# 日期符合条件,那么统计目标 Num 增加 1
# 循环结束后,输出统计值
print(f'2018-2021年,共有{Num}个家庭农场成立。')
# 输出:2018-2021年,共有182855个家庭农场成立。
💡 小贴士:Python 中日期格式并不是简单的字符串,而是一个时间戳对象,其中包含年、月、日、时、分、秒等属性。在上面这个场景中,我们只使用年份属性(date.year)就足以达成目的。
2无限循环——while 循环语句
与 for 循环不同,while 循环不涉及循环体,也没有明确的循环次数,在 while 循环开始之前需要设置一个终止循环的条件。只有当满足这个终止条件,循环才会终止。while 循环的语法格式和示意图如下。
while <循环终止条件>:
<语句块>
在上面的 while 循环结构或示意图中,语句块中的内容最好能够直接影响循环终止条件,如果语句块中的内容对终止条件的判断毫无影响,那么 while 循环会有可能陷入死循环。
在上图的 while 循环中,终止条件 number > 1永远是成立的,语句print(number)对终止条件毫无影响。那么在这个程序被关闭或机器坏掉之前,这个循环将会永远执行下去。我们把程序变换一下,添加一行代码,每次循环让 number
Part5主动退出循环与主动跳过循环
在循环结构中,除了让循环程序自动结束之外,我们还可以使用 break 关键字或 continue
1主动退出循环——break
无论是在 for 循环中还是在 while 循环中,都可以使用 break 关键字直接跳出整个循环结构,执行循环之外的代码。如果有多层循环(循环中还有循环),则会跳出 break
上图所示程序是我们在介绍 while 循环时用到的死循环,我们在死循环中加入条件,当程序执行时间大于等于 5 秒钟,就执行 break 语句跳出循环。在程序最后,变量 number
这个关键字/语句有什么实际用处呢?在实际的编程中,经常需要测试代码或者修改代码中的 BUG,如果我们在循环的最后面加上一句 break,那么程序在执行完第一次循环后就会主动结束。这时我们可以方便地查看程序这一次循环中的变量值,进而快速定位、解决问题,不再需要等待整个循环结束。除此之外,当在循环程序中已经达到既定目的后,也可以使用 break 语句退出循环,这就和 while 循环有些相似了。
2主动跳过循环——continue
continue 语句的功能是中断/跳过本次循环,直接进入下一次循环。下面我们通过一个场景简单了解 continue 语句的功能。
【场景 2】在全国所有在市场监管部门登记在册的家庭农场数据中,统计出成立时间在 2018-2021年之间且在 2021 年底仍是在营状态的家庭农场数量。
【解决方式】主要思路是先查看企业状态是否是在营,如果不是,那么直接跳过这次循环,进入下一次,不再继续判断该家庭农场的成立时间是否符合要求。如果是是在营状态,则再查看成立时间。代码如下。
# 将表中成立时间、企业状态两字段分别转为列表
ESDATE = list(data['成立时间'])
STATE = list(data['企业状态(2021年底)'])
# 初始化目标统计值
Num = 0
# 使用 for 同时循环遍历列表 ESDATE 和 STATE
for date,state in zip(ESDATE, STATE):
# date 是成立日期, state 是企业状态
# 如果企业状态不是“在营(开业)企业”,那么直接跳过本次循环,不再判断成立时间是否符合要求
if state != '在营(开业)企业':
continue
# 判断企业成立时间是否符合要求
if 2018 <= date.year <= 2021:
Num += 1 # 日期符合条件,那么统计目标 Num 增加 1
# 循环结束后,输出统计
print(f'2018-2021年,共有{Num}个家庭农场成立且至少存活至2021年底。')
# 输出:2018-2021年,共有174351个家庭农场成立且至少存活至2021年底。
Part6结束语
限于篇幅,本期文章只介绍了 Python 中循环结构的一部分内容。下期文章我们将继续介绍循环结构,学习程序中的异常处理以及更加优雅的循环和分支语法。