前面两课,我们把 Python 的变量、类型、运算符这些基础语法过了一遍,已经能写一些从上往下执行的简单脚本了。但从这一课开始,Python 才真正开始“有脑子”——流程控制就是给程序装上判断力和循环力的关键一课。你可以把它理解成给代码写“如果……就……否则……”的规则,以及“重复做某件事 N 次”的复读机逻辑。无论你是零基础入门,还是跟着教程学过一点但总是写不出自己的逻辑,这一课的内容都能帮你把“看得懂代码”变成“写得出逻辑”。
流程控制是编程语言的分水岭,凡是牵扯到条件判断、循环遍历、分支选择的场景——比如登录验证、数据清洗、爬虫翻页、批量处理文件——背后全是它的影子。这一课的定位就是承上启下,把 if、for、while、break、continue 这些关键字彻底讲清楚,配合几个上手就能跑的练习,让你在学完这一课后,遇到实际问题时第一反应不是“怎么写”,而是“我怎么用流程把这个问题拆开”。
这篇内容我按从易到难的顺序来安排,先讲分支判断,再讲循环遍历,然后用两个实战小项目把知识点串起来,最后补充异常处理和避坑指南。每一段我都会把“为什么这么写”讲明白,而不是只丢给你一堆语法。
1. 流程控制的整体认知
1.1 为什么说流程控制是编程的分水岭
很多新手写代码,前期靠的是“背语法”,但到了流程控制这一课,死记硬背就失灵了。因为流程控制要求你从“认识单词”过渡到“组织句子”,程序不再是一条直线跑到黑,而是根据不同的输入、不同的状态走不同的路。
打个比方,你点外卖的决策过程就是一套流程控制:如果余额充足就下单,否则就换一家或者放弃;如果商家超时未接单就自动取消,否则等待配送。每一步都是一个“条件判断”,而取餐过程中反复查看配送进度就是一个“循环”。编程里的流程控制干的就是同样的事,只不过决策者和执行者都是计算机而已。
Python 的流程控制主要包含三块:顺序结构、分支结构、循环结构。顺序结构你已经会了,就是代码从上到下一行行执行;分支结构用 if 系列关键字实现;循环结构用 for 和 while 实现。这一课的重点就是后两块,它们是程序逻辑复杂度的主要来源。
1.2 学完这一课你应该掌握哪些东西
我按实际的掌握程度给你列一个目标清单,建议学完后对照自测:
- 能用 if、elif、else 组合出多条件分支,并且不会把逻辑顺序搞乱。
- 能用 for 循环遍历字符串、列表、range 序列,知道循环变量在每一轮的值。
- 能用 while 循环写出带退出条件的重复操作,并避开死循环的坑。
- 能用 break、continue 精确控制循环的“跳出”和“跳过”。
- 能读懂循环嵌套,并且自己写出类似九九乘法表这种经典练习。
- 知道 try/except 能做基本的异常处理,让程序在出错时不至于直接崩溃。
如果你能达到这份清单的一半,流程控制这一关就算过了;如果全部达到,你已经有能力解决很多日常脚本问题了。下面我们一项一项来。
2. 条件分支 if/elif/else 的进阶用法
2.1 最简单的 if 语句与缩进的“生死线”
先看一个最基础的例子,判断一个数是否为偶数:
number = 10 if number % 2 == 0: print("这是一个偶数")这段代码的逻辑很直白:如果 number 除以 2 的余数是 0,就打印“这是一个偶数”。但新手最容易在“缩进”上栽跟头。Python 不像其他语言用大括号表示代码块,它用缩进来表示。写在 if 下面且缩进了的代码,才属于这个 if 的执行范围。
注意:同一个代码块内,缩进必须保持一致。如果混用 Tab 和空格,会让解释器直接报错
IndentationError。我见过不少新手卡在这个问题上,其实解法很简单——你的编辑器里统一把 Tab 键设置为“插入4个空格”,以后就再也不会出现这种诡异错误了。
if 语句后面一定要写冒号,这个冒号是 Python 语法的一部分,代表“接下来是条件成立时要执行的代码块”。忘了冒号同样会报语法错误。这种小细节说多了显得啰嗦,但报错报多了你就知道,九成新手 bug 都是这两类:少了冒号、缩进不对。
2.2 多条件判断:elif 和 else 的配合顺序
现实中的判断很少只有“是/否”两种情况。比如给成绩分等级,90 分以上是 A,80 到 89 是 B,60 到 79 是 C,60 以下是 D。这时候就需要 if、elif、else 全家桶:
score = 85 if score >= 90: grade = "A" elif score >= 80: grade = "B" elif score >= 60: grade = "C" else: grade = "D" print(grade)这段代码的关键在于执行顺序。Python 会从上往下依次判断每个条件,一旦遇到某个条件为 True,就执行对应的代码块,然后跳过整个 if-elif-else 结构,不会再往下判断。所以条件的排列顺序非常重要。
你把条件反过来写试试:先写if score >= 60,再写elif score >= 80,结果 85 分也会被归到 C 档,因为第一个条件就成立了。这就是“顺序决定结果”的典型例子。写多分支判断的时候,建议把最严格、最特殊的条件放在最前面,范围宽泛的放在后面,这样逻辑才不会打架。
还有一个新手经常搞混的地方:==是判断相等,=是赋值。if 条件里写if score = 85会直接语法错误,因为=是赋值操作,不是一个“比较”。这个错误不算隐蔽,但我每次带新手都会强调一次——条件判断里永远用双等号。
2.3 真值判断的“隐形规则”
Python 里有个很贴心的特性:不只是 True 和 False 可以作为判断条件,很多对象也能直接放在 if 后面。比如数字 0、空字符串 ""、空列表 []、空字典 {}、None,这些值在布尔上下文中都会被当作 False,其他值则会被当作 True。
这意味着你可以写出很简洁的判断:
name = input("请输入你的名字:") if name: print(f"你好,{name}!") else: print("你好像什么都没输入。")这里没有显式写name != "",但效果一样。因为空字符串会被判断为 False,非空字符串是 True。这种写法简洁且符合英文阅读习惯,但我建议你刚开始学的时候还是写完整一点——if name != ""——让逻辑更直白。等你熟悉了真值判断的规则,再改成简写也不迟。
2.4 三目运算符与 match-case 的“现代写法”
有时候你只是想根据条件选一个值,不需要写一大段 if-else。比如根据分数判断“及格/不及格”,用三目运算符可以压缩成一行:
result = "及格" if score >= 60 else "不及格"这个写法其实就是条件为真时取的值 if 条件 else 条件为假时取的值。它适合简单的二选一场景,一旦分支变多还是老老实实用 if-elif-else 更好,否则一行代码长得像绕口令,别人读起来费劲。
如果你用的是 Python 3.10 以上版本,还可以试试 match-case 语句,它类似其他语言里的 switch,适合做多值匹配。比如根据用户输入的命令执行不同操作:
command = input("请输入命令:") match command: case "start": print("开始运行") case "stop": print("停止运行") case _: print("未知命令")最后的case _相当于 else,匹配所有未列出的情况。这个语法挺时髦,但第三课阶段了解一下即可,实际项目中时常用 if 也能实现同样的效果。
3. for 循环和 while 循环怎么选
3.1 for 循环的本质:遍历迭代器
for 循环是 Python 里最常用的循环,它的本质是“遍历一个可迭代对象”。字符串、列表、元组、字典、range 序列都是可迭代对象,你都可以用 for 把它们里面的元素一个一个取出来。
fruits = ["苹果", "香蕉", "橙子"] for fruit in fruits: print(fruit)这段代码做的事情很直观:第一次循环时 fruit 等于“苹果”,第二次等于“香蕉”,第三次等于“橙子”。循环体执行几次,取决于列表里有几个元素。for 循环的优点是你不用自己维护“循环次数”这个变量,Python 自动帮你迭代完整个序列。
实际工作中最常见的 for 循环场景是和 range 函数配合,生成一个数字序列:
for i in range(5): print(i)range(5) 生成的是 0 到 4 这五个数字,注意不包含 5。这是编程里一个非常经典的“从 0 开始”习惯,新手刚接触时经常算错边界。你可以用 range(1, 6) 显式指定从 1 开始到 5 结束,第三个参数还可以控制步长,比如 range(0, 10, 2) 生成 0、2、4、6、8。
排查建议:如果你的循环结果里“少了一个数”或者“多了一个数”,先检查 range 的起止边界。range 的区间是左闭右开,也就是包含起始值、不包含结束值。记住这一条,能省不少调试时间。
3.2 while 循环的使用场景与死循环避坑
while 循环和 for 循环最大的区别在于:for 明确知道要循环多少次,而 while 是“只要条件成立就一直循环”。什么时候用 while 更合适?典型场景是你不知道循环什么时候结束——比如用户输入的密码直到正确为止、游戏角色血量降到 0 为止。
password = "" while password != "python123": password = input("请输入密码:") print("密码正确,进入系统")这段代码会一直要求用户输入,直到输入的字符串等于“python123”才退出循环。这里有一个新手必踩的坑:while 循环的条件判断中,一定要有某个变量在循环体内被改变,否则条件永远成立,程序就成了死循环。
我见过一个很经典的死循环:
i = 1 while i <= 5: print(i) # 忘记写 i += 1这段代码会一直打印 1,因为 i 永远不增加,条件i <= 5永远为 True。遇到这种情况,终端会疯狂刷屏,你只能按 Ctrl+C 强制终止程序。写 while 循环时,养成一个好习惯:先问自己“循环体内哪一行代码负责让条件最终变成 False”,如果答不上来,大概率就会写出死循环。
3.3 break、continue、else 的搭配规则
循环只有“跑完”和“跑不完”两种状态,但有时候你想在中间干预一下。break 和 continue 就是干这个的:break 是提前退出整个循环,continue 是跳过当前这一轮、直接进入下一轮。
for i in range(1, 10): if i == 3: continue if i == 7: break print(i)这段代码会打印 1、2、4、5、6,然后当 i 等于 7 时触发 break,循环彻底结束。注意 continue 跳过了 i 等于 3 的那一轮,所以 3 没有打印出来。区分这两个关键字有一个通俗的记忆方法:break 是“破门而出”,continue 是“跳过这一关继续前进”。
Python 的循环还有一个很少人知道但很实用的特性——循环还可以带 else。这个 else 不是和 if 搭配的那个 else,它表示“循环正常结束后执行一次”。如果循环被 break 打断了,else 就不会执行。
numbers = [1, 3, 5, 7, 9] for n in numbers: if n % 2 == 0: print("找到了偶数:", n) break else: print("列表里没有偶数")这个特性特别适合“找东西”的场景,比如在一组数据里搜索符合条件的元素,找到了就 break 退出,没找到就执行 else 里的提示逻辑。很多其他语言没有这个语法,所以如果你是别的语言转过来的,可能会觉得有点新鲜。建议刻意用几次,就能体会到它的简洁。
4. 实战练习:从“看懂”到“写出来”
4.1 经典案例:猜数字小游戏
说一千道一万,不如动手写一段完整的程序。我首选推荐猜数字游戏,因为它能同时锻炼 if、while、break、随机数、类型转换等知识点。规则很简单:程序随机生成一个 1 到 100 的整数,用户输入数字猜测,程序提示“大了”“小了”或“猜对了”。
import random target = random.randint(1, 100) count = 0 while True: guess = input("请输入你猜的数字(1-100):") if not guess.isdigit(): print("请输入一个有效的整数。") continue guess = int(guess) count += 1 if guess > target: print("大了,再猜小一点。") elif guess < target: print("小了,再猜大一点。") else: print(f"恭喜你,猜对了!你一共猜了 {count} 次。") break这段代码有几个值得注意的细节。第一,input返回的是字符串,必须用int()转成整数才能和 target 比较。第二,用户可能输入非数字,所以用isdigit()先做合法性校验,不合法就 continue 跳过这一轮。第三,while True 配合 break 是“先进入循环再判断是否退出”的经典写法,非常适合人机交互场景。
这个游戏你也可以扩展一下:增加最大猜测次数,超过次数提示失败;加入难度选择,简单模式 1-50、困难模式 1-1000;记录历史最佳成绩。每扩展一个功能,你就相当于多练了一次流程控制的组合运用。
4.2 经典案例:九九乘法表与循环嵌套
九九乘法表是练习嵌套循环的经典题目。嵌套循环就是循环里面套循环,外层循环每执行一轮,内层循环会完整执行一遍。打印九九乘法表的逻辑是:外层循环负责行数(1 到 9),内层循环负责每行打印几个式子。
for i in range(1, 10): for j in range(1, i + 1): print(f"{j} × {i} = {i * j}", end="\t") print()注意内层循环的 range 是range(1, i + 1),也就是说第 i 行只打印 i 个式子。这样第 1 行只有一个“1×1=1”,第 9 行有九个式子。end="\t"表示打印完不换行,而是以制表符结尾,这样同一行的几个式子能并排显示;内层循环结束后再调用一个空的print()换行。
运行效果大概是:
1 × 1 = 1 1 × 2 = 2 2 × 2 = 4 1 × 3 = 3 2 × 3 = 6 3 × 3 = 9嵌套循环的核心是把“循环变量”之间的关系理清楚。外层变量 i 控制行数,内层变量 j 控制列数,第 i 行第 j 列的值就是j × i。如果你用代码打印一个矩形或三角形图案,思路也是完全一致的。新手第一次接触嵌套循环容易晕,建议拿笔在纸上画几行推导一下,把 i 和 j 的值写出来,比空想快得多。
4.3 综合小练习:判断一个数是不是素数
如果你觉得九九乘法表太简单,可以尝试写一个“判断素数”的功能。素数就是只能被 1 和自身整除的大于 1 的自然数,比如 2、3、5、7、11。判断一个数 n 是不是素数,只需要从 2 试到 n 的平方根,看有没有能整除它的数。
import math n = int(input("请输入一个大于1的整数:")) is_prime = True if n <= 1: is_prime = False else: for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: is_prime = False break if is_prime: print(f"{n} 是素数") else: print(f"{n} 不是素数")这里用到了 for-else 的逻辑变体:如果循环过程中找到了因子,break 退出,is_prime 被设为 False;如果循环正常结束,说明没有因子能够整除 n,is_prime 保持 True。当然,显式使用一个布尔变量来控制状态,对新手来说更直观,理解起来压力更小。
你可能会问,为什么只需判断到sqrt(n)就够了?因为如果 n 有一个大于平方根的因子 a,那么必然存在一个小于平方根的小因子 b,使得 a×b=n。也就是说,只要检查到平方根位置都没找到因子,再往上检查也是多余的。这个思路在算法优化里很常见——通过数学性质缩小搜索范围,避免不必要的循环消耗。
5. 异常处理与逻辑简化的小技巧
5.1 try/except 让程序“优雅地出错”
第三课虽然主要讲流程控制,但我强烈建议你把 try/except 这一课提前学掉。原因是你在实操中一定会遇到用户乱输入、文件不存在、网络超时这些情况,如果没做异常处理,程序直接崩溃退出,体验很差。异常处理的本质也是流程控制——正常流程怎么走,出错流程怎么走。
看一个例子,把字符串转成整数,用户如果输入“abc”就会抛 ValueError:
while True: text = input("请输入一个整数:") try: number = int(text) break except ValueError: print("这不是一个整数,请重新输入。")这个写法比前面用的isdigit()更通用,因为有些字符串isdigit()判断为 True,但int()照样可能失败。try 块里的代码是“冒险操作”,如果抛出异常,程序不会崩溃,而是跳到 except 块执行对应处理;如果没异常,就跳过 except 继续往下走。对于入门阶段,你只需要掌握这一个模式就够用了。
经常有人问 try/except 和 if 判断哪个更好。我的经验是:能提前用 if 判断的,先用 if 避免异常;无法提前预判的错误(比如文件被占用、网络断连),再用 try/except 兜底。两者不是替代关系,而是互补关系。
5.2 列表推导式:用一行代码替换简单循环
列表推导式是一个非常“Pythonic”的写法,它把 for 循环和列表创建合并成一行表达式。比如生成 1 到 10 的所有偶数平方:
squares = [i * i for i in range(1, 11) if i % 2 == 0] print(squares)这个式子等价于:
squares = [] for i in range(1, 11): if i % 2 == 0: squares.append(i * i)列表推导式的语法结构是[表达式 for 变量 in 可迭代对象 if 条件],if 部分可以省略。我建议你在完全掌握普通 for 循环之后再接触这个写法,因为列表推导式虽然简洁,但对新手来说可读性差,一个复杂的推导式能让你半天看不懂。实际开发中,我一般只在逻辑简单的时候用它,复杂逻辑还是老老实实写普通循环。
5.3 enumerate 与 zip:让循环更顺手
遍历列表时,很多时候你既需要元素值,又需要元素下标。用最原始的方式写,你需要引入一个计数器变量:
fruits = ["苹果", "香蕉", "橙子"] index = 0 for fruit in fruits: print(index, fruit) index += 1有了 enumerate,这个操作就优雅多了:
for index, fruit in enumerate(fruits): print(index, fruit)enumerate 会返回一个包含“下标”和“值”的二元组,用两个变量同时接收即可。还有一种常见场景是同时遍历两个列表,比如把学生姓名和分数一一对应:
names = ["张三", "李四", "王五"] scores = [90, 85, 78] for name, score in zip(names, scores): print(f"{name} 的分数是 {score}")这两组工具函数能减少你写循环时的样板代码,让逻辑更清晰。它们也是 Python 开发者日常使用频率非常高的内置函数,建议这一课就直接记住。
6. 常见问题与排查技巧实录
6.1 死循环的快速定位与处理
死循环大概是流程控制阶段最让人抓狂的问题。如果程序在终端里一直刷输出、没有任何停止迹象,大概率就是 while 循环的条件永远为真,或者 for 循环的遍历对象本身有问题。
排查死循环我有一套固定流程。第一步,先按 Ctrl+C 强制终止程序,观察报错信息里停在哪个文件哪一行,那里往往就是循环体内部。第二步,检查 while 循环的条件变量是否在循环体内部被更新,如果有 break 语句,检查 break 前面是否满足了合适的条件。第三步,在循环体里临时加几个 print,把关键变量的值打印出来,看它是否朝着预期的方向变化。定位到是哪一行导致条件一直为真,修复方案也就自然出来了。
6.2 缩进错误与逻辑错误的区分
缩进错误(IndentationError)是最容易排查的错误,Python 编译器会直接告诉你“expected an indented block”或者“unexpected indent”,看着报错提示就能找到问题。真正的难点是逻辑错误——程序不报错,但结果就是不对。
我举一个最常见的逻辑错误场景:判断年份是否为闰年。正确的规则是“能被 4 整除但不能被 100 整除,或者能被 400 整除”,新手很容易漏掉“不能被 100 整除”这个条件,导致 1900 年这种年份被误判。这类错误编译器不会发现,只有测试用例才能暴露。所以写完代码之后,一定要准备多组边界数据自测,比如最小的输入值、最大的输入值、临界值等。流程控制写的不是“让程序跑起来”,而是“让程序在每一种情况下都跑对”。
6.3 变量作用域与循环变量的“残留”问题
Python 的 for 循环变量有一个特性:循环结束后,循环变量仍然存在,值等于最后一次循环的值。这个和某些其他语言不一样,容易带来隐藏 bug。比如:
for i in range(5): pass print(i) # 输出 4,i 仍然存在这个问题的典型场景是:循环次数为 0 时,循环变量根本不会被赋值,后面再用它就会报 NameError。所以,不要依赖循环之后循环变量的值,如果需要保留结果,就在循环之前初始化一个变量。
while 循环类似,如果循环的条件判断依赖某个外部变量,而这个变量没有被正确初始化,程序可能直接跳过循环体,或者一进来就满足退出条件。不管是哪种循环,写完多问一句:如果循环一次都没执行,程序会怎样?这个习惯能帮你避免很多条件边界的坑。
6.4 关于流程控制的最后一点心得
我自己带新手学 Python 的时候,发现流程控制这一课最容易出现两个极端:一类同学觉得太简单,看一遍就跳过,结果遇到实际问题不知道用哪种循环;另一类同学陷入语法细节,总想找一个“万能模板”,但逻辑一变就懵了。其实流程控制的核心不在语法,而在于“把问题拆成顺序、分支、循环三种结构”的能力。
你可以试着用这个方法来训练自己:拿到一个实际问题,先不写代码,用大白话把流程说一遍,比如“先判断输入是否合法,如果合法就计算,否则提示错误;计算的结果放进列表里,一直算到达到目标数量为止”。流程说清楚了,翻译成 Python 只是时间问题。写代码卡住的时候,不妨停下来,把逻辑重新讲一遍,往往讲着讲着问题就自己暴露了。
流程控制练熟之后,你会发现自己已经能读懂不少实用的脚本了。下一步可以尝试写一些解决实际小事的程序,比如批量重命名文件、定时提醒、验证码生成器,这些项目会在后面的课程里一一展开,但它们的底子都是这一课的分支与循环。最后分享一个我自己的习惯:每写一个循环,都问自己一句——这个程序有没有可能在某个输入下永远退出不了?这个习惯帮我挡掉过无数次生产事故,希望你也能用上。