Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
1 | 作者:李晓辉 |
- Python 系列(一) 从零开始认识 Python,入门其实没那么难
- Python 系列(二) 学 Python 的第一步,把开发环境搭建好
- Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
- Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
- Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
- Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
- Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
- Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
- Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
- Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
前面我们学习了 Python 的数据类型、条件判断以及逻辑运算符。现在,程序已经能够根据不同条件执行不同操作了。
但如果需要检查 100 台服务器,难道要把检查命令复制 100 遍吗?如果需要逐行分析一个包含几十万条记录的日志文件,难道也要手动处理每一行吗?
显然不现实。
这时候就需要循环结构。我们可以让程序重复执行某段代码,直到满足退出条件,或者遍历完所有需要处理的数据。
今天我们就来学习 Python 中的
while和for循环,以及控制循环执行过程的break、continue和range()。最后,我们还会结合字符串拆分、批量运维和分页处理,看看循环究竟能解决哪些实际问题。
while 循环:满足条件就继续执行
while 循环适合在某个条件成立时,反复执行一组语句。
它的基本语法如下:
1 | while condition: |
执行逻辑是:
- 首先判断
condition是否为真。 - 如果为真,执行缩进在
while下面的语句。 - 执行完循环体后,重新判断条件。
- 如果条件仍然为真,就继续执行。
- 如果条件变为假,就结束循环,执行循环后面的代码。
这里与上一章的 if 有一个明显区别:if 通常只判断一次,而 while 会在每次迭代开始时重新判断条件。
使用 while 计算 5 到 10 的总和
先看一个简单的累加程序。
1 | #!/usr/bin/env python3 |
运行结果:
1 | Running Total = 5 Counter = 6 |
我们拆开来看。
首先:
1 | counter = 5 |
counter 用来记录当前处理到哪个数字,total 用来保存累加结果。
第一次循环时:
1 | total += counter |
相当于:
1 | total = total + counter |
因此,第一次循环后,total 变成 5,counter 变成 6。
接下来,程序重新检查:
1 | while counter <= 10: |
因为 counter 是 6,条件仍然成立,所以继续累加。
当 counter 为 10 时,程序仍然会执行循环体,将 10 加入总和,然后把 counter 增加到 11。
下一次检查条件时:
1 | 11 <= 10 |
结果为假,循环结束。
所以,最终总和是 45,而 counter 的最终值是 11。
注意:counter += 1 是这个循环能够正常结束的关键。 如果没有这条语句,counter 会一直保持为 5,条件始终成立,程序就会不断重复执行。
while 循环的执行流程
flowchart TD
A["初始化变量"] --> B{"while 条件成立?"}
B -->|是| C["执行循环体"]
C --> D["更新变量"]
D --> B
B -->|否| E["执行循环后面的代码"]这张图可以帮助我们理解 while 的本质:判断、执行、更新、再次判断。
写 while 时,一定要明确三个问题:
- 循环什么时候开始?
- 每次循环会发生什么变化?
- 满足什么条件时退出?
如果没有设计好变量更新或退出条件,就可能出现无限循环。
为什么 while True 可能造成死循环?
在 Python 中,True 是一个始终为真的布尔值。
因此:
1 | while True: |
会不断输出 Hello,因为循环条件始终成立。
这种循环通常被称为无限循环。
但需要注意,无限循环不一定是错误的,缺少合理退出机制才可能造成问题。
例如,长期运行的服务程序、消息消费者和持续监控任务,可能本来就需要一直运行。此时可以通过 break、退出信号或其他停止机制来控制程序结束。
一个常见错误:忘记更新循环变量
下面这段代码就有问题:
1 | counter = 1 |
由于 counter 一直是 1,条件始终成立,程序会不断打印 1。
正确写法:
1 | counter = 1 |
输出:
1 | 1 |
这里的 counter += 1 使变量逐步增加,最终让循环条件变为假。
使用 while True 主动控制退出
有时,我们无法提前确定循环应该执行多少次。例如,程序需要不断接收用户输入,直到用户决定退出。
可以这样写:
1 | while True: |
当用户输入 quit 时,break 会立即结束当前循环。
这种写法在交互式工具、持续监控和事件处理程序中都很常见。
不过,在生产环境中,长期运行的程序还需要考虑异常处理、资源释放以及系统信号等问题。while True 本身并不会自动处理这些事情。
break 与 continue:改变循环的执行流程
前面我们已经使用了 break。现在再来认识另一个常见关键字:continue。
它们都能改变循环的执行流程,但作用完全不同。
| 关键字 | 作用 | 执行后发生什么 |
|---|---|---|
break | 结束当前循环 | 跳转到循环后面的代码 |
continue | 跳过本次迭代的剩余部分 | 进入下一次迭代 |
break:满足条件后立即结束循环
例如,我们想在数字序列中找到第一个大于 10 的数字:
1 | for number in range(1, 20): |
输出:
1 | 找到了: 11 |
当 number 变成 11 时,条件成立,程序执行 break,直接退出循环。
后面的 12、13、14 等数字都不会再处理。
这在查找符合条件的记录时非常有用。例如,检查服务列表时,如果只需要找到第一个符合条件的服务,就不必继续遍历剩余数据。
需要注意,break 只会结束它所在的最内层循环。如果存在嵌套循环,它不会自动结束所有外层循环。
continue:跳过本次迭代
例如,我们希望打印 1 到 10 中所有不是 3 的倍数的数字。
1 | for number in range(1, 11): |
输出:
1 | 1 |
这里的:
1 | if number % 3 == 0: |
表示当数字可以被 3 整除时,跳过本次循环剩余的语句。
例如,number 为 3 时,程序执行 continue,因此不会执行后面的 print(number),随后继续处理下一个数字 4。
这在过滤数据时很有用。例如,分析日志文件时,可以跳过空行、注释行或者不需要处理的记录。
continue 为什么可能造成死循环?
continue 并不是每次都安全。
看下面的代码:
1 | counter = 1 |
当 counter 变成 3 时,程序执行 continue,直接跳过了下面的:
1 | counter += 1 |
于是 counter 永远停留在 3,循环条件始终成立,程序就陷入无限循环。
正确的写法是先更新变量,再判断是否跳过本次迭代:
1 | counter = 1 |
输出:
1 | 1 |
这里将当前值保存到 current,并提前更新 counter,即使执行 continue,下一次循环也能够正常推进。
当然,如果只是遍历固定序列,使用 for 通常更简单。但这个例子说明了一个重要原则:在 while 循环中,必须确保每条可能的执行路径都能让循环条件最终发生变化,或者有明确的退出机制。
for 循环:遍历序列中的每一个元素
与 while 不同,for 循环主要用于遍历可迭代对象中的元素。
例如:
- 字符串中的每个字符。
- 列表中的每个元素。
range()产生的每个整数。- 文件中的每一行。
- 字典中的键或键值对。
基本语法:
1 | for variable in iterable: |
Python 会依次从可迭代对象中取出元素,将当前元素赋给变量 variable,然后执行循环体。
当元素全部遍历完毕后,循环结束。
遍历字符串
1 | word = "Hello" |
输出:
1 | H |
这里,for 每次取出一个字符,并将它赋给 each_character。
第一次是 H,第二次是 e,接下来依次是 l、l 和 o。
因此,for 并不只是循环数字,它可以遍历字符串等可迭代对象。
for 与 while 应该怎么选?
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 遍历服务器列表 | for | 每台服务器处理一次 |
| 遍历日志文件的每一行 | for | 按行处理数据 |
| 累加固定范围的数字 | for 或 while | 两种都可以,for 通常更简洁 |
| 等待某个状态满足条件 | while | 退出时机取决于条件 |
| 持续监听事件 | while | 循环可能长期运行 |
| 遍历固定次数的任务 | for + range() | 次数清晰,不容易遗漏更新 |
简单来说,如果你需要遍历一个已有的数据集合,优先考虑 for;如果循环是否继续取决于某个动态条件,可以考虑 while。
range:为什么 range(5, 10) 不包含 10?
range() 是 Python 中非常常用的内置函数,可以生成一个整数序列,常用于控制 for 循环的次数。
它支持三种常见形式:
1 | range(stop) |
参数的含义如下:
start:起始值,默认是 0。stop:结束边界,但不包含这个值。step:步长,默认是 1。
range(5):从 0 开始
1 | for i in range(5): |
输出:
1 | 0 |
range(5) 相当于从 0 开始,直到 5 之前结束。
因此,它一共产生 5 个整数,而不是 6 个。
range(5, 10):包含起点,不包含终点
1 | for i in range(5, 10): |
输出:
1 | 5 |
为什么不包含 10?
因为 range() 的结束边界采用左闭右开区间,也就是包含起始值,不包含结束值。
可以将它理解为:
$$
5 \leq i < 10
$$
这种设计能够让区间长度更容易计算。例如,range(5, 10) 一共有 5 个整数。
在实际编程中,左闭右开的区间非常常见。理解这个规则后,你就不容易在循环次数、数组下标和分页处理中出现越界错误。
range 的第三个参数:step
第三个参数用于指定每次递增或递减的步长。
例如:
1 | for i in range(-5, 9, 3): |
输出:
1 | -5 -2 1 4 7 |
程序从 -5 开始,每次加 3:
- 第一次:-5
- 第二次:-2
- 第三次:1
- 第四次:4
- 第五次:7
下一次是 10,但 10 已经不小于结束边界 9,因此停止。
步长也可以为负数:
1 | for i in range(10, 0, -2): |
输出:
1 | 10 8 6 4 2 |
这里从 10 开始,每次减去 2,并在到达或越过结束边界 0 时停止。
需要注意,如果步长为 0,range() 会抛出 ValueError。如果使用负步长,却把起始值设置得小于结束值,通常会得到空序列。
字符串 split:把一段文本拆分成多个部分
在 Linux 运维工作中,我们经常需要处理文本。
例如:
- 将一组服务器名称拆分成独立条目。
- 将逗号分隔的配置项拆分成多个值。
- 将日志内容按照空白字符拆分。
- 将命令输出转换为便于逐项处理的数据。
Python 的字符串方法 split() 就能帮助我们完成这些工作。
不指定分隔符:按照空白字符拆分
1 | text = """Each word is separated |
输出:
1 | Each |
这里没有向 split() 传递参数,因此 Python 会按照连续的空白字符拆分字符串,包括空格、制表符和换行符等,并忽略首尾的空白。
这比手动按照一个空格拆分更适合处理不规则文本。
例如:
1 | text = " Linux Python\tAutomation " |
输出:
1 | ['Linux', 'Python', 'Automation'] |
即使单词之间有多个空格或者制表符,也能正常拆分。
指定分隔符:按照逗号拆分
如果数据使用逗号分隔,就可以指定 ,:
1 | text = "Linux,Python,Automation" |
输出:
1 | Linux |
这里的:
1 | text.split(",") |
表示按照逗号拆分字符串。
需要注意,指定分隔符与不指定分隔符的行为并不完全相同。例如,指定逗号时,连续的逗号可能产生空字符串:
1 | text = "Linux,,Python," |
输出:
1 | ['Linux', '', 'Python', ''] |
这在解析配置文件或简单文本格式时尤其值得注意,因为空字符串可能代表缺失的数据。
另外,split() 返回的是一个列表。虽然本章还没有专门介绍列表,但你已经可以使用 for 遍历它。后续学习列表时,我们会进一步解释如何存储、访问和修改这些元素。
运维实战一:使用循环批量检查服务器
前面我们已经知道,for 可以遍历字符串和数字序列。现在,我们用它来模拟批量检查服务器。
1 | servers = [ |
运行结果:
1 | 正在检查服务器:server01 |
这里的 servers 是一个列表,for 会依次取出每个服务器名称。
当然,目前程序只是打印名称,还没有真正连接服务器。但在真实项目中,可以将循环体扩展为调用 SSH、执行远程命令、采集监控指标或记录检查结果。
跳过不需要检查的服务器
假设某些服务器处于维护状态,我们不希望检查它们。
1 | servers = [ |
输出:
1 | 正在检查服务器:server01 |
当 server 为 server02 时,程序执行 continue,跳过本次循环剩余的检查操作,然后继续处理下一台服务器。
这就是 continue 在批量运维中的典型用途:跳过不需要处理的对象,而不是结束整个任务。
运维实战二:使用 while 实现分页处理
当需要处理大量数据时,很多 API 不会一次返回所有记录,而是采用分页机制。
例如,总共有 103 条记录,每页最多处理 20 条。
我们希望输出每一页的记录范围:
1 | 页 1: 1-20 |
可以使用下面的代码:
1 | total_items = 103 |
运行结果:
1 | 页 1: 1-20 |
我们来分析一下关键代码。
首先:
1 | start = (page - 1) * page_size + 1 |
用于计算当前页的起始记录编号。
第一页的起始编号为 1,第二页为 21,第三页为 41,以此类推。
接下来:
1 | end = min(page * page_size, total_items) |
用于计算当前页的结束编号。
min() 会返回两个值中较小的一个。因此,最后一页即使不足 20 条,也不会超过总记录数 103。
最后,循环条件:
1 | while (page - 1) * page_size < total_items: |
用于判断当前页是否还有记录可以处理。
当页码增加到 7 时,起始记录编号将是 121,已经超过总记录数,因此循环结束。
这里还有一个值得注意的边界问题:如果只写 page * page_size <= total_items,就会漏掉不足整页的最后一部分数据。对于分页任务,循环条件必须考虑最后一页是否还有记录。
实际调用 API 时,还需要处理分页参数、网络错误、服务端返回的总记录数变化,以及是否存在下一页等情况。本例只是演示分页范围的计算逻辑。
while 与 for 的常见错误
忘记更新 while 的循环变量
1 | counter = 1 |
问题:counter 始终为 1,循环永远不会结束。
解决办法:在合适的位置更新变量,或者使用明确的退出条件。
误以为 range 包含结束值
1 | for i in range(1, 5): |
结果是:
1 | 1 |
不会输出 5,因为 range() 不包含结束边界。
在不恰当的位置使用 continue
如果 continue 跳过了负责更新循环变量的代码,就可能造成无限循环。
对于 while,要确保循环变量或状态在每次迭代中能够按预期变化。
混淆 break 与 continue
break 是退出整个当前循环;continue 只是跳过本次迭代剩余的代码。
如果想跳过一条不符合条件的日志,通常使用 continue;如果已经找到目标、无需继续搜索,则可以使用 break。
忘记循环变量的含义
例如:
1 | for i in range(5): |
输出
1 | 0 |
这里的 i 只是一个变量名,并不是 Python 特殊的循环关键字。
你也可以写成:
1 | for number in range(5): |
如果变量代表服务器、日志行或者文件名,最好使用具有实际含义的名称,而不是所有场景都使用 i。
总结
本篇我们学习了 Python 中两种常见的循环结构。
while 根据条件决定是否继续执行,适合循环次数不确定、退出时机取决于状态的场景。
for 用于遍历可迭代对象,适合处理服务器列表、日志记录、字符串和固定范围的数字序列。
我们还学习了:
break:立即结束当前循环。continue:跳过本次迭代的剩余语句。range():生成整数序列,起始值包含在内,结束值不包含在内。split():将字符串拆分为多个部分,再通过for逐项处理。- 循环变量与退出条件:避免无限循环和遗漏数据。
对于 Linux 运维人员来说,循环是自动化的基础。掌握它之后,就可以把单台服务器的检查逻辑扩展为批量检查,把单条日志的处理扩展为逐行分析,把一次数据处理扩展为分页获取。
