1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com
  1. Python 系列(一) 从零开始认识 Python,入门其实没那么难
  2. Python 系列(二) 学 Python 的第一步,把开发环境搭建好
  3. Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
  4. Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
  5. Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
  6. Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
  7. Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
  8. Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
  9. Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
  10. Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue

前面我们学习了 Python 的数据类型、条件判断以及逻辑运算符。现在,程序已经能够根据不同条件执行不同操作了。

但如果需要检查 100 台服务器,难道要把检查命令复制 100 遍吗?如果需要逐行分析一个包含几十万条记录的日志文件,难道也要手动处理每一行吗?

显然不现实。

这时候就需要循环结构。我们可以让程序重复执行某段代码,直到满足退出条件,或者遍历完所有需要处理的数据。

今天我们就来学习 Python 中的 while 和 for 循环,以及控制循环执行过程的 break、continue 和 range()。最后,我们还会结合字符串拆分、批量运维和分页处理,看看循环究竟能解决哪些实际问题。

while 循环:满足条件就继续执行

while 循环适合在某个条件成立时,反复执行一组语句。

它的基本语法如下:

1
2
3
4
5
while condition:
statement_1
statement_2

statement_3

执行逻辑是:

  1. 首先判断 condition 是否为真。
  2. 如果为真,执行缩进在 while 下面的语句。
  3. 执行完循环体后,重新判断条件。
  4. 如果条件仍然为真,就继续执行。
  5. 如果条件变为假,就结束循环,执行循环后面的代码。

这里与上一章的 if 有一个明显区别:if 通常只判断一次,而 while 会在每次迭代开始时重新判断条件。

使用 while 计算 5 到 10 的总和

先看一个简单的累加程序。

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3

counter = 5
total = 0

while counter <= 10:
total += counter
counter += 1
print("Running Total =", total, "Counter =", counter)

print()
print("Final Total =", total)

运行结果:

1
2
3
4
5
6
7
8
Running Total = 5 Counter = 6
Running Total = 11 Counter = 7
Running Total = 18 Counter = 8
Running Total = 26 Counter = 9
Running Total = 35 Counter = 10
Running Total = 45 Counter = 11

Final Total = 45

我们拆开来看。

首先:

1
2
counter = 5
total = 0

counter 用来记录当前处理到哪个数字,total 用来保存累加结果。

第一次循环时:

1
2
total += counter
counter += 1

相当于:

1
2
total = total + counter
counter = counter + 1

因此,第一次循环后,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
2
while True:
print("Hello")

会不断输出 Hello,因为循环条件始终成立。

这种循环通常被称为无限循环。

但需要注意,无限循环不一定是错误的,缺少合理退出机制才可能造成问题。

例如,长期运行的服务程序、消息消费者和持续监控任务,可能本来就需要一直运行。此时可以通过 break、退出信号或其他停止机制来控制程序结束。

一个常见错误:忘记更新循环变量

下面这段代码就有问题:

1
2
3
4
counter = 1

while counter <= 5:
print(counter)

由于 counter 一直是 1,条件始终成立,程序会不断打印 1。

正确写法:

1
2
3
4
5
counter = 1

while counter <= 5:
print(counter)
counter += 1

输出:

1
2
3
4
5
1
2
3
4
5

这里的 counter += 1 使变量逐步增加,最终让循环条件变为假。

使用 while True 主动控制退出

有时,我们无法提前确定循环应该执行多少次。例如,程序需要不断接收用户输入,直到用户决定退出。

可以这样写:

1
2
3
4
5
6
7
8
9
while True:
command = input("请输入命令,输入 quit 退出:")

if command == "quit":
break

print("你输入的是:", command)

print("程序已退出")

当用户输入 quit 时,break 会立即结束当前循环。

这种写法在交互式工具、持续监控和事件处理程序中都很常见。

不过,在生产环境中,长期运行的程序还需要考虑异常处理、资源释放以及系统信号等问题。while True 本身并不会自动处理这些事情。

break 与 continue:改变循环的执行流程

前面我们已经使用了 break。现在再来认识另一个常见关键字:continue。

它们都能改变循环的执行流程,但作用完全不同。

关键字作用执行后发生什么
break结束当前循环跳转到循环后面的代码
continue跳过本次迭代的剩余部分进入下一次迭代

break:满足条件后立即结束循环

例如,我们想在数字序列中找到第一个大于 10 的数字:

1
2
3
4
5
6
for number in range(1, 20):
if number > 10:
print("找到了:", number)
break

print("循环结束")

输出:

1
2
找到了: 11
循环结束

当 number 变成 11 时,条件成立,程序执行 break,直接退出循环。

后面的 12、13、14 等数字都不会再处理。

这在查找符合条件的记录时非常有用。例如,检查服务列表时,如果只需要找到第一个符合条件的服务,就不必继续遍历剩余数据。

需要注意,break 只会结束它所在的最内层循环。如果存在嵌套循环,它不会自动结束所有外层循环。

continue:跳过本次迭代

例如,我们希望打印 1 到 10 中所有不是 3 的倍数的数字。

1
2
3
4
5
for number in range(1, 11):
if number % 3 == 0:
continue

print(number)

输出:

1
2
3
4
5
6
7
1
2
4
5
7
8
10

这里的:

1
2
if number % 3 == 0:
continue

表示当数字可以被 3 整除时,跳过本次循环剩余的语句。

例如,number 为 3 时,程序执行 continue,因此不会执行后面的 print(number),随后继续处理下一个数字 4。

这在过滤数据时很有用。例如,分析日志文件时,可以跳过空行、注释行或者不需要处理的记录。

continue 为什么可能造成死循环?

continue 并不是每次都安全。

看下面的代码:

1
2
3
4
5
6
7
8
counter = 1

while counter <= 5:
if counter == 3:
continue

print(counter)
counter += 1

当 counter 变成 3 时,程序执行 continue,直接跳过了下面的:

1
counter += 1

于是 counter 永远停留在 3,循环条件始终成立,程序就陷入无限循环。

正确的写法是先更新变量,再判断是否跳过本次迭代:

1
2
3
4
5
6
7
8
9
10
counter = 1

while counter <= 5:
current = counter
counter += 1

if current == 3:
continue

print(current)

输出:

1
2
3
4
1
2
4
5

这里将当前值保存到 current,并提前更新 counter,即使执行 continue,下一次循环也能够正常推进。

当然,如果只是遍历固定序列,使用 for 通常更简单。但这个例子说明了一个重要原则:在 while 循环中,必须确保每条可能的执行路径都能让循环条件最终发生变化,或者有明确的退出机制。

for 循环:遍历序列中的每一个元素

与 while 不同,for 循环主要用于遍历可迭代对象中的元素。

例如:

  • 字符串中的每个字符。
  • 列表中的每个元素。
  • range() 产生的每个整数。
  • 文件中的每一行。
  • 字典中的键或键值对。

基本语法:

1
2
3
for variable in iterable:
statement_1
statement_2

Python 会依次从可迭代对象中取出元素,将当前元素赋给变量 variable,然后执行循环体。

当元素全部遍历完毕后,循环结束。

遍历字符串

1
2
3
4
word = "Hello"

for each_character in word:
print(each_character)

输出:

1
2
3
4
5
H
e
l
l
o

这里,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
2
3
range(stop)
range(start, stop)
range(start, stop, step)

参数的含义如下:

  • start:起始值,默认是 0。
  • stop:结束边界,但不包含这个值。
  • step:步长,默认是 1。

range(5):从 0 开始

1
2
for i in range(5):
print(i)

输出:

1
2
3
4
5
0
1
2
3
4

range(5) 相当于从 0 开始,直到 5 之前结束。

因此,它一共产生 5 个整数,而不是 6 个。

range(5, 10):包含起点,不包含终点

1
2
for i in range(5, 10):
print(i)

输出:

1
2
3
4
5
5
6
7
8
9

为什么不包含 10?

因为 range() 的结束边界采用左闭右开区间,也就是包含起始值,不包含结束值。

可以将它理解为:

$$
5 \leq i < 10
$$

这种设计能够让区间长度更容易计算。例如,range(5, 10) 一共有 5 个整数。

在实际编程中,左闭右开的区间非常常见。理解这个规则后,你就不容易在循环次数、数组下标和分页处理中出现越界错误。

range 的第三个参数:step

第三个参数用于指定每次递增或递减的步长。

例如:

1
2
for i in range(-5, 9, 3):
print(i, end=" ")

输出:

1
-5 -2 1 4 7

程序从 -5 开始,每次加 3:

  • 第一次:-5
  • 第二次:-2
  • 第三次:1
  • 第四次:4
  • 第五次:7

下一次是 10,但 10 已经不小于结束边界 9,因此停止。

步长也可以为负数:

1
2
for i in range(10, 0, -2):
print(i, end=" ")

输出:

1
10 8 6 4 2

这里从 10 开始,每次减去 2,并在到达或越过结束边界 0 时停止。

需要注意,如果步长为 0,range() 会抛出 ValueError。如果使用负步长,却把起始值设置得小于结束值,通常会得到空序列。

字符串 split:把一段文本拆分成多个部分

在 Linux 运维工作中,我们经常需要处理文本。

例如:

  • 将一组服务器名称拆分成独立条目。
  • 将逗号分隔的配置项拆分成多个值。
  • 将日志内容按照空白字符拆分。
  • 将命令输出转换为便于逐项处理的数据。

Python 的字符串方法 split() 就能帮助我们完成这些工作。

不指定分隔符:按照空白字符拆分

1
2
3
4
5
6
7
text = """Each word is separated
by whitespace"""

data = text.split()

for value in data:
print(value)

输出:

1
2
3
4
5
6
Each
word
is
separated
by
whitespace

这里没有向 split() 传递参数,因此 Python 会按照连续的空白字符拆分字符串,包括空格、制表符和换行符等,并忽略首尾的空白。

这比手动按照一个空格拆分更适合处理不规则文本。

例如:

1
2
3
text = "  Linux    Python\tAutomation  "

print(text.split())

输出:

1
['Linux', 'Python', 'Automation']

即使单词之间有多个空格或者制表符,也能正常拆分。

指定分隔符:按照逗号拆分

如果数据使用逗号分隔,就可以指定 ,:

1
2
3
4
5
6
text = "Linux,Python,Automation"

data = text.split(",")

for value in data:
print(value)

输出:

1
2
3
Linux
Python
Automation

这里的:

1
text.split(",")

表示按照逗号拆分字符串。

需要注意,指定分隔符与不指定分隔符的行为并不完全相同。例如,指定逗号时,连续的逗号可能产生空字符串:

1
2
3
text = "Linux,,Python,"

print(text.split(","))

输出:

1
['Linux', '', 'Python', '']

这在解析配置文件或简单文本格式时尤其值得注意,因为空字符串可能代表缺失的数据。

另外,split() 返回的是一个列表。虽然本章还没有专门介绍列表,但你已经可以使用 for 遍历它。后续学习列表时,我们会进一步解释如何存储、访问和修改这些元素。

运维实战一:使用循环批量检查服务器

前面我们已经知道,for 可以遍历字符串和数字序列。现在,我们用它来模拟批量检查服务器。

1
2
3
4
5
6
7
8
servers = [
"server01",
"server02",
"server03",
]

for server in servers:
print(f"正在检查服务器:{server}")

运行结果:

1
2
3
正在检查服务器:server01
正在检查服务器:server02
正在检查服务器:server03

这里的 servers 是一个列表,for 会依次取出每个服务器名称。

当然,目前程序只是打印名称,还没有真正连接服务器。但在真实项目中,可以将循环体扩展为调用 SSH、执行远程命令、采集监控指标或记录检查结果。

跳过不需要检查的服务器

假设某些服务器处于维护状态,我们不希望检查它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
servers = [
"server01",
"server02",
"server03",
]

maintenance_servers = {"server02"}

for server in servers:
if server in maintenance_servers:
print(f"跳过维护中的服务器:{server}")
continue

print(f"正在检查服务器:{server}")

输出:

1
2
3
正在检查服务器:server01
跳过维护中的服务器:server02
正在检查服务器:server03

当 server 为 server02 时,程序执行 continue,跳过本次循环剩余的检查操作,然后继续处理下一台服务器。

这就是 continue 在批量运维中的典型用途:跳过不需要处理的对象,而不是结束整个任务。

运维实战二:使用 while 实现分页处理

当需要处理大量数据时,很多 API 不会一次返回所有记录,而是采用分页机制。

例如,总共有 103 条记录,每页最多处理 20 条。

我们希望输出每一页的记录范围:

1
2
3
4
5
6
页 1: 1-20
页 2: 21-40
页 3: 41-60
页 4: 61-80
页 5: 81-100
页 6: 101-103

可以使用下面的代码:

1
2
3
4
5
6
7
8
9
10
total_items = 103
page_size = 20
page = 1

while (page - 1) * page_size < total_items:
start = (page - 1) * page_size + 1
end = min(page * page_size, total_items)

print(f"页 {page}: {start}-{end}")
page += 1

运行结果:

1
2
3
4
5
6
页 1: 1-20
页 2: 21-40
页 3: 41-60
页 4: 61-80
页 5: 81-100
页 6: 101-103

我们来分析一下关键代码。

首先:

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
2
3
4
counter = 1

while counter <= 5:
print(counter)

问题:counter 始终为 1,循环永远不会结束。

解决办法:在合适的位置更新变量,或者使用明确的退出条件。

误以为 range 包含结束值

1
2
for i in range(1, 5):
print(i)

结果是:

1
2
3
4
1
2
3
4

不会输出 5,因为 range() 不包含结束边界。

在不恰当的位置使用 continue

如果 continue 跳过了负责更新循环变量的代码,就可能造成无限循环。

对于 while,要确保循环变量或状态在每次迭代中能够按预期变化。

混淆 break 与 continue

break 是退出整个当前循环;continue 只是跳过本次迭代剩余的代码。

如果想跳过一条不符合条件的日志,通常使用 continue;如果已经找到目标、无需继续搜索,则可以使用 break。

忘记循环变量的含义

例如:

1
2
for i in range(5):
print(i)

输出

1
2
3
4
5
0
1
2
3
4

这里的 i 只是一个变量名,并不是 Python 特殊的循环关键字。

你也可以写成:

1
2
for number in range(5):
print(number)

如果变量代表服务器、日志行或者文件名,最好使用具有实际含义的名称,而不是所有场景都使用 i。

总结

本篇我们学习了 Python 中两种常见的循环结构。

while 根据条件决定是否继续执行,适合循环次数不确定、退出时机取决于状态的场景。

for 用于遍历可迭代对象,适合处理服务器列表、日志记录、字符串和固定范围的数字序列。

我们还学习了:

  • break:立即结束当前循环。
  • continue:跳过本次迭代的剩余语句。
  • range():生成整数序列,起始值包含在内,结束值不包含在内。
  • split():将字符串拆分为多个部分,再通过 for 逐项处理。
  • 循环变量与退出条件:避免无限循环和遗漏数据。

对于 Linux 运维人员来说,循环是自动化的基础。掌握它之后,就可以把单台服务器的检查逻辑扩展为批量检查,把单条日志的处理扩展为逐行分析,把一次数据处理扩展为分页获取。