Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
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 的基本数据类型,了解了字符串、整数、浮点数以及类型转换。
但程序光能处理数据还不够。比如,我们编写了一个 CPU 监控脚本:CPU 使用率低于 50% 时显示正常,达到 50% 时进入警告级别,达到 80% 时升级为严重告警,达到或超过 95% 时提示危险。计算机怎么知道应该执行哪一段代码?
这就需要用到 Python 的流程控制结构。
不过,在学习
if之前,我们还需要解决一个看似简单、实际上非常重要的问题:为什么 Python 对缩进如此严格?在 Linux 运维中,一个缩进错误可能导致告警逻辑失效,甚至让本来只应该执行一次的操作被重复执行。今天我们就从缩进开始,逐步掌握 Python 的决策结构,并通过实际例子理解它在运维自动化中的应用。
为什么 Python 必须使用缩进?
如果你之前接触过 Shell、C 或 Java,可能已经习惯使用大括号 {} 来表示代码块。
例如,Shell 可以这样写:
1 | if [ "$cpu" -gt 80 ]; then |
而 Python 不需要 then、fi 或大括号,只需要冒号和缩进:
1 | cpu = 85 |
看起来简单了不少,但 Python 对代码格式也提出了明确要求。
在 Python 中,缩进不仅仅是为了让代码看起来整齐,它还是程序语法的一部分。
缩进决定代码属于哪个代码块
先看一个例子:
1 | cpu = 85 |
运行结果:
1 | CPU 使用率过高 |
这里有三个需要注意的地方。
if cpu > 80:是条件判断的标头。- 前面有 4 个空格的两条
print()语句属于if代码块。 - 最后一条
print()没有缩进,属于外层代码,因此不受这个if条件控制。
换句话说,当 cpu > 80 成立时,Python 会执行缩进在 if 下面的两条语句;无论条件是否成立,最后一条 print() 都会执行。
我们可以把代码的执行结构理解为:
flowchart TD
A["开始"] --> B["cpu = 85"]
B --> C{"cpu > 80 ?"}
C -->|是| D["打印 CPU 使用率过高"]
D --> E["打印检查提示"]
C -->|否| F["跳过 if 代码块"]
E --> G["打印监控程序执行完毕"]
F --> G这也是为什么学习 Python 时,不能只关注每一行代码写了什么,还要关注它的缩进位置。
为什么一个缩进错误就可能导致程序报错?
先看一段错误代码:
1 | cpu = 85 |
运行后,Python 会报出类似下面的错误:
1 | File "<python-input-3>", line 4 |
原因很简单:if 后面需要一个代码块,但是 Python 没有找到正确缩进的语句。
再看一个例子:
1 | cpu = 85 |
第二条 print() 比第一条多缩进了一层,但前面并没有新的代码块来接收它,因此可能出现:
1 | File "<python-input-5>", line 5 |
注意,并不是少一个空格就一定会报错。真正的要求是:同一代码块中的语句必须具有一致的缩进层级,新的缩进层级必须符合语法结构。
例如,下面的代码也是错误的:
1 | if cpu > 80: |
输出
1 | IndentationError: unindent does not match any outer indentation level |
同一个代码块中的两条语句缩进不一致,Python 无法将它们正确归入同一语句组。
Python 推荐使用 4 个空格
根据 PEP 8,也就是 Python 官方推荐的代码风格指南,每一级缩进推荐使用 4 个空格。
例如:
1 | if cpu > 80: |
这里出现了两个层级:
- 第一层
if下面的语句缩进 4 个空格。 - 第二层
if下面的语句缩进 8 个空格。
对于初学者,建议遵守以下规范:
- 每一级缩进使用 4 个空格。
- 不要在同一个代码块中混用 Tab 和空格。
- 使用 VS Code、PyCharm 等编辑器时,开启自动缩进。
- 不要为了让代码看起来对齐,随意修改某一行的缩进。
Tab 键不一定等于 4 个空格。编辑器可以将一个 Tab 显示成多个空格宽度,但它在文件中仍然可能是一个制表符。如果同一个代码块混用了 Tab 和空格,Python 可能报出 TabError。
对于 Linux 运维脚本,建议从一开始就统一使用 4 个空格。这样代码更容易阅读,也更便于多人协作。
认识复合语句:冒号到底有什么用?
在 Python 中,if、for、while、def 等语句都可以引出一个代码块。这样的结构称为复合语句。
复合语句通常由两部分组成:
- 标头(header):例如
if cpu > 80:。 - 语句组(suite):也就是标头下面缩进的代码。
例如:
1 | if cpu > 80: |
这里的冒号 : 表示标头结束,后面开始一个语句组。
如果遗漏冒号:
1 | if cpu > 80 |
Python 会报出 SyntaxError,因为条件判断的语法不完整。
1 | File "<python-input-10>", line 1 |
所以,看到 if、elif 或 else 时,可以先检查两个地方:
- 标头末尾有没有冒号。
- 下一行的缩进是否正确。
对于刚开始学习 Python 的同学,这两项检查能够解决不少常见问题。
if:让程序根据条件作出判断
前面我们已经知道,缩进决定了代码块的范围。那么,代码块什么时候执行呢?
答案是由条件表达式决定。
if 的基本语法如下:
1 | if condition: |
当 condition 的结果为真时,执行缩进在 if 下面的语句;当条件为假时,跳过这个代码块,继续执行后面的代码。
先看一个最简单的例子。
1 | cpu = 85 |
输出
1 | CPU 使用率过高 |
因为 85 > 80 成立,所以第一条 print() 会执行,最后一条 print() 也会执行。
如果将 cpu 修改为 50,第一条 print() 就会被跳过,但监控程序执行完毕这句话仍然会输出。
Python 支持哪些比较运算符?
编写条件判断时,经常会使用比较运算符。
| 运算符 | 含义 | 示例 |
|---|---|---|
== | 等于 | cpu == 80 |
!= | 不等于 | cpu != 80 |
> | 大于 | cpu > 80 |
< | 小于 | cpu < 80 |
>= | 大于或等于 | cpu >= 80 |
<= | 小于或等于 | cpu <= 80 |
需要特别注意,= 和 == 的含义不同。
1 | cpu = 80 |
这里的 = 用于赋值,将 80 赋给变量 cpu。
而:
1 | cpu == 80 |
这里的 == 用于比较,判断 cpu 是否等于 80,结果是一个布尔值 True 或 False。
初学者经常会将二者混淆,尤其是在编写复杂条件时。
为什么建议先赋值,再统一打印?
我们来看一个与运维监控有关的例子。
假设我们输入一个数字,并判断它与目标值 50 的关系。
第一种写法:
1 | number = int(input("请输入一个整数:")) |
这段代码完全正确,但两条分支都调用了一次 print()。
如果后续还要输出判断结果、记录日志、生成监控报告,就可能出现更多重复代码。
我们可以将变化的内容先保存到变量中:
1 | number = int(input("请输入一个整数:")) |
这样,无论进入哪个分支,最后都只需要调用一次 print()。
例如,输入 30:
1 | 请输入一个整数:30 |
输入 70:
1 | 请输入一个整数:70 |
这种写法体现了一个值得养成的编程习惯:当多个分支最终需要完成相同的操作时,可以先计算出不同的结果,再统一执行公共操作。
这与 DRY 原则有关。DRY 是 Don’t Repeat Yourself 的缩写,强调尽量减少不必要的重复。
当然,并不是所有场景都必须先赋值再打印。如果不同分支需要执行完全不同的操作,直接在分支内部执行也很合理。关键是让代码结构清晰,而不是机械地追求少写一行。
elif:多个条件应该怎么判断?
如果只有两种情况,使用 if 和 else 通常就够了。
但在实际运维工作中,我们经常需要区分多种状态。
例如:
- CPU 使用率低于 50%:正常。
- CPU 使用率达到 50%,但低于 80%:注意观察。
- CPU 使用率达到 80%,但低于 95%:严重。
- CPU 使用率达到或超过 95%:危险。
如果只使用 if 和 else,代码就会变得复杂。这时可以使用 elif。
elif 是 else if 的缩写,表示前面的条件不成立时,继续判断另一个条件。
基本语法:
1 | if condition_1: |
执行过程可以概括为:
- 首先检查
condition_1。 - 如果成立,执行第一段代码,跳过其余分支。
- 如果不成立,继续检查
condition_2。 - 按照相同的方式依次判断后续的
elif。 - 如果所有条件都不成立,并且存在
else,就执行else代码块。
这里有一个非常重要的特点:在同一组 if、elif、else 结构中,最多只有一个分支会被执行。
一个完整的多分支示例
1 | value = int(input("请输入一个整数:")) |
输入 12,运行结果:
1 | 请输入一个整数:12 |
为什么程序没有继续判断后面的条件?
因为 value <= 15 成立后,Python 就执行对应的代码块,并跳过剩余分支。
这里还有一个值得注意的地方:第二个条件只写了 value <= 10,没有写 value > 5。
这是因为程序只有在第一个条件 value <= 5 不成立时,才会继续判断第二个条件。此时已经可以确定 value > 5。
同理,第三个条件也不必再写 value > 10。
这种写法能够让代码更加简洁,但它依赖于分支的判断顺序。如果将条件顺序随意调换,程序的结果就可能发生变化。
为什么条件顺序如此重要?
看下面的错误示例:
1 | cpu = 96 |
你可能会认为,CPU 使用率为 96,应该输出危险。
但实际输出是:
1 | 正常 |
为什么?
因为 96 >= 50 已经成立,Python 进入第一个分支后,就不会继续检查后面的 elif。
因此,多分支判断必须考虑条件之间的包含关系。对于这种从低到高的阈值判断,应该从较低阈值开始使用递增上界,或者按照从高到低的顺序判断。
例如:
1 | cpu = 96 |
输出:
1 | 危险 |
这次程序会先检查最高阈值,命中后立即结束整个分支结构。
实际编写监控程序时,建议先明确每个状态的范围,再决定判断顺序。不要只看条件本身是否正确,还要检查前面的条件会不会提前截获本该属于后面分支的数据。
else:处理剩下的情况
else 用于处理前面所有条件都不成立的情况。
例如:
1 | cpu = 35 |
输出
1 | CPU 使用率未达到告警阈值 |
由于 cpu >= 80 不成立,程序执行 else 分支。
需要注意,else 后面不需要再写条件,但必须加冒号。
另外,else 不是必需的。如果程序只关心某个条件成立时该做什么,也可以只使用 if:
1 | cpu = 85 |
输出
1 | CPU 使用率过高 |
当 CPU 使用率低于 80% 时,程序什么也不做,然后继续执行后面的代码。
这在运维脚本中非常常见。例如,只有发现异常时才发送告警,正常情况下不输出额外信息。
运维实战:编写一个 CPU 使用率分级程序
前面的示例都是为了理解语法。现在,我们将 if、elif 和 else 组合起来,写一个更接近实际工作的 CPU 使用率分级程序。
第一版:实现基本分级
1 | cpu = float(input("请输入 CPU 使用率:")) |
运行示例:
1 | 请输入 CPU 使用率:85 |
这里有两个新知识点。
首先,float() 可以将符合格式的数字字符串转换为浮点数,因此我们可以输入 85.5 这样的数值。
其次,最后两条语句使用了 f-string,也就是格式化字符串。f"CPU 使用率:{cpu}%" 中的 {cpu} 会被替换为变量的值。
这种方式非常适合输出监控信息、日志内容和运行报告。
为什么这个例子不需要写完整的范围?
你可能发现,代码没有写成:
1 | if cpu >= 0 and cpu < 50: |
而是直接使用:
1 | if cpu < 50: |
这是因为 elif 只有在前面条件不成立时才会执行。
对于输入 85:
cpu < 50不成立。cpu < 80不成立。cpu < 95成立。
因此,程序会将状态设置为“严重”。
不过,这段代码有一个前提:我们假设输入的 CPU 使用率是合法的,且处于 0 到 100 之间。真实的运维程序不能只考虑正常输入,还应该处理非法输入。
第二版:增加输入合法性检查
例如,用户可能输入:
abc:不是有效的数字。-10:小于合法范围。120:超过 CPU 使用率的正常取值范围。
我们可以先将类型转换放在 try 代码块中,再进行范围检查:
1 | try: |
这段代码同时引入了 try 和 except,用于捕获类型转换时可能发生的 ValueError。
例如,输入 abc 时,float() 无法完成转换,程序就会执行 except 中的错误提示,而不是直接抛出异常并终止。
而输入 120 时,类型转换本身没有问题,但范围检查会发现这个值超出了允许范围。
这说明两件事情并不相同:
- 类型合法性:输入的内容能不能转换成数字。
- 业务合法性:转换后的数字是否符合程序要求。
真实的监控程序通常需要同时检查这两项。
边界值测试:50%、80%、95% 到底属于哪个状态?
写阈值判断时,最容易遗漏的问题之一就是边界值。
我们将 CPU 分级规则整理成表格:
| CPU 使用率 | 判断结果 |
|---|---|
| 0% | 正常 |
| 49.9% | 正常 |
| 50% | 警告 |
| 79.9% | 警告 |
| 80% | 严重 |
| 94.9% | 严重 |
| 95% | 危险 |
| 100% | 危险 |
注意,cpu < 50 与 cpu <= 50 的区别非常重要。
cpu < 50不包含 50。cpu <= 50包含 50。
如果没有明确边界条件,就可能出现某个数值被分到错误的级别。
在生产环境中,建议至少测试每个阈值的前一个值、阈值本身和后一个值。对于浮点数,可以使用 49.9、50、50.1 这样的测试数据。
还要注意,示例中的 0 到 100 范围是针对 CPU 百分比的简化假设。真实监控系统可能会因为采样方式、容器 CPU 配额或指标定义不同而使用其他判断规则。
Linux 运维中的实际应用
掌握了条件判断,就可以开始思考:它到底能帮助我们完成哪些运维任务?
例如,日常工作中经常需要检查磁盘使用率、内存使用率、服务状态和系统负载。
根据磁盘使用率决定是否告警
假设我们已经从监控系统中获取了某个文件系统的使用率:
1 | disk_usage = 92 |
输出
1 | 警告:磁盘使用率较高 |
如果磁盘使用率达到 92%,程序会输出警告。
这里需要注意,示例中的 disk_usage = 92 是模拟数据,并没有真正读取 Linux 文件系统的使用率。
在实际项目中,我们可以进一步使用 Python 的 subprocess 模块调用 df -h,或者使用 shutil.disk_usage() 获取文件系统容量信息,再根据结果进行判断。
根据服务状态决定下一步操作
假设我们通过其他方式获得了 Nginx 的运行状态:
1 | service_status = "inactive" |
输出
1 | Nginx 未处于运行状态,请检查服务 |
这个示例演示了字符串比较。
真实环境中,服务状态可以通过 systemctl is-active nginx 获取。之后,程序就可以根据检查结果记录日志、发送告警,或者触发进一步的诊断流程。
但需要特别注意:发现服务异常,并不意味着应该立即自动重启服务。 生产环境还需要考虑故障原因、重启策略、重试次数和业务影响,避免错误的自动化操作扩大故障。
为什么缩进错误在运维脚本中尤其危险?
假设你编写了一个批量检查服务器的程序,希望只有在发现磁盘空间不足时才输出告警。
如果条件判断写错,可能出现两种结果:
- 正常服务器也不断产生告警,导致大量误报。
- 真正发生异常时,没有执行告警逻辑,导致问题未被及时发现。
当脚本进一步涉及删除文件、重启服务或批量修改配置时,错误的条件和缩进还可能产生更严重的后果。
因此,运维脚本不能只关注能否运行,还要关注条件覆盖是否完整、边界值是否正确,以及每一条操作是否处于正确的代码块中。
if、elif、else 的几个常见误区
把多个独立的 if 当成一个多分支结构
看下面的代码:
1 | number = 12 |
输出:
1 | 大于 5 |
这是因为两个 if 是相互独立的判断,条件成立时都可以执行。
而使用 if 和 elif:
1 | number = 12 |
输出:
1 | 大于 5 |
第一个条件已经成立,所以第二个分支不会再执行。
因此,选择 if 还是 elif,取决于业务逻辑:你是希望多个条件分别判断,还是只允许其中一个分支执行?
认为 else 必须跟在每个 if 后面
这是错误的。
下面的代码完全合法:
1 | cpu = 90 |
输出
1 | CPU 使用率过高 |
如果条件不成立,程序就跳过 if 代码块,不需要额外的 else。
忘记缩进会影响哪些语句执行
例如:
1 | cpu = 90 |
输出
1 | CPU 使用率过高 |
前两条打印语句受 if 控制,最后一条打印语句不受它控制。
阅读代码时,可以尝试从 if 的标头向下观察,直到遇到缩进层级回到外层的语句。这样就能判断哪些操作属于当前分支。
认为 elif 是 Python 唯一的多分支选择方式
在 Python 3.10 及更高版本中,还可以使用 match 和 case 处理某些多分支场景。
例如:
1 | status = "warning" |
输出
1 | 需要关注 |
这里的 match 更适合根据值的模式进行匹配。对于 CPU 数值阈值这种需要比较大小的场景,使用 if 和 elif 往往更直观。
对于初学者,建议先熟练掌握 if、elif 和 else,再学习 match。
总结:让程序真正具备判断能力
今天我们学习了 Python 中非常重要的决策结构。
首先,缩进决定代码块的范围。Python 使用缩进表达语句之间的层级关系,推荐每一级使用 4 个空格。
其次,if 用于判断条件是否成立,elif 用于在前面的条件不成立时继续判断其他条件,else 用于处理剩余情况。
最后,在编写实际程序时,不仅要保证语法正确,还要注意条件顺序、边界值和输入合法性。
对于 Linux 运维人员来说,这些知识可以帮助我们逐步编写自动化检查脚本,例如磁盘空间告警、服务状态检查和 CPU 使用率分级。
不过,目前我们的程序仍然需要手动输入数据,或者使用提前写好的模拟值。下一步,我们将学习如何使用 Python 获取真实的系统信息,并把条件判断与 Linux 命令、系统接口结合起来。
当程序能够主动获取数据、判断状态并采取合适的行动时,它才真正开始具备运维自动化的能力。
