让代码学会“看情况办事”:Python分支与循环精讲 程序这东西很多初学者刚接触的时候都会有一个错觉程序不就是从上到下、一行一行按顺序执行吗这说的没错但那只是最基础的顺序结构。真实世界里没有哪款软件是纯直线跑下来的——你点外卖时程序要根据订单金额判断要不要收配送费你登录账号时程序要根据你输入的密码判断对不对你刷短视频时程序要根据你的点击行为决定给你推什么内容。这些根据条件做不同处理的能力靠的就是分支和循环。没错这一讲要聊的就是编程里怎么让代码长脑子。这一讲是系列教程的第三讲前两讲应该已经把变量、数据类型、输入输出这些地基打好了。如果你还没掌握那些内容建议先回去复习一下因为今天讲的分支和循环本质上就是把前面那些基础语法串起来的骨架。我会用大量贴近实际场景的例子带你把 if、else、elif、while、for 这几个核心关键字彻底吃透。全程我会用 Python 作为演示语言它的语法最接近自然语言初学阶段不会被语法细节绊住脚。1. 分支的本质让程序学会看情况办事1.1 从线性执行到条件跳转先想一个最基本的问题顺序执行的代码就像一条单行道不管前面是畅通还是堵车都得一路开到底。而分支结构相当于在这条路上开了几个岔路口程序运行到岔路口时会先停下来看一眼路况再决定往哪条路走。这个看路况的动作在编程里叫条件判断。判断的结果只有两种成立或不成立也就是 True 或 False。听起来很简单对吧但几乎所有复杂的业务逻辑最终都可以拆解成无数个这样的小判断组合。我举个特别直白的例子。假设你要写一个程序根据用户输入的年龄判断他属于哪个年龄段age int(input(请输入你的年龄)) if age 18: print(未成年人) else: print(成年人)这段代码运行到if age 18这一行时程序会先计算age 18这个表达式的值。如果用户输入的是 15这个表达式的结果就是 True程序走第一个分支打印未成年人如果用户输入的是 20表达式结果是 False程序走 else 分支打印成年人。你发现没有整个过程中代码绕了一下路。这就是分支的意义——不是所有代码都要被执行而是根据条件选择性地执行其中一部分。用一句人话总结分支让代码有了选择权。1.2 布尔值与比较运算判断的地基刚才说到条件判断的结果是 True 或 False这两个值在编程里专门有个名字叫布尔值bool。它们是一切分支判断的基石。布尔值从哪来最常见的就是通过比较运算产生。比如运算含义示例结果相等5 5True!不相等5 ! 3True大于7 3True小于7 3False大于等于5 5True小于等于5 4False这里有个初学者最容易踩的坑和是完全不同的东西。是赋值是把右边的值装进左边的变量是比较是判断左右两边是否相等。很多人一上来就把 if 语句写成if age 18然后程序直接报错或者出现诡异逻辑问题就在这。除了比较运算还有一种生成布尔值的方式就是逻辑运算——and、or、not。这三个词的意思跟日常英语几乎一样a and ba 和 b 同时为 True结果才是 Truea or ba 和 b 只要有任意一个为 True结果就是 Truenot a把 True 变成 False把 False 变成 True举个例子你想判断一个人是否可以买成人票且不是老年人假设老年指 65 岁以上if age 18 and age 65: print(可以购买全价成人票)这个逻辑用一句大白话翻译就是年龄不小于 18并且年龄小于 65两个条件同时满足才成立。逻辑运算最实用的地方在于它能把多个判断条件组合成一个更复杂的判断让分支结构真正处理现实世界中那些并不简单的规则。2. if 家族的三种形态单分支、双分支、多分支2.1 最基础的 if不需要 else 的单行道闸机有些场景下你只需要在条件成立时做某件事条件不成立时什么都不做这时候单分支的 if 就够了。比如说一个网上商城系统当订单金额超过一定数额时就给用户免运费total float(input(请输入订单金额)) if total 99: print(恭喜已为您免去运费。) print(订单处理完成。)注意这个例子里print(订单处理完成。)这行没有缩进它不属于 if 语句块不管条件成立与否都会执行。而真正属于 if 的只有print(恭喜已为您免去运费。)这一行。这儿必须强调一下 Python 一个极其重要的语法特点缩进就是代码块的分界。在 C、Java 这些语言里代码块用花括号{}包裹在 Python 里同一个缩进层级的就是同一个代码块。这一设计让 Python 代码读起来特别清爽但也让很多新手在这个地方翻车。我自己见过太多次这样的报错了IndentationError: expected an indented block after if statement翻译过来就是if 语句后面必须有一个缩进的代码块。换句话说if 下面不能什么都不写。如果你暂时还没想好条件下该做什么可以用pass占位它表示什么都不做if total 99: pass # 后续再补充免邮逻辑2.2 if-else非此即彼的岔路口双分支比单分支多了一个 else代表条件不成立时走另一条路。它的执行流程非常好理解if 的条件是 True执行 if 下面的代码块是 False执行 else 下面的代码块。二选一没有中间地带。之前那个年龄判断就是典型的 if-else。我再给个稍微实际点的例子——判断一个整数是奇数还是偶数num int(input(请输入一个整数)) if num % 2 0: print(f{num} 是偶数) else: print(f{num} 是奇数)这里用到了取余运算符%。num % 2的意思是 num 除以 2 的余数如果余数为 0说明能被 2 整除那就是偶数。这个场景在真实项目里出现频率极高——不只是判断奇偶任何需要按余数分流的逻辑基本都是这个思路。if-else 有一个非常隐蔽的坑需要留意if 和 else 必须保持在同一个缩进级别。因为 else 是和 if 配对的如果你把 else 的缩进搞乱了程序要么报错要么逻辑完全变样——它可能会错误地挂到了别的 if 上。2.3 elif多条件分流的阶梯现实中的判断往往不止两种情况。比如给考试成绩分等级90 分以上是优秀80 到 89 是良好60 到 79 是及格60 以下是不及格。这种多档位的判断就需要 Python 提供的 elif 语法。score int(input(请输入考试成绩)) if score 90: print(优秀) elif score 80: print(良好) elif score 60: print(及格) else: print(不及格)这段代码的判读逻辑是从上往下依次判断先看score 90如果成立直接输出优秀后面的 elif 和 else 全部跳过如果不成立再看score 80……一直到某个条件成立为止如果所有条件都不成立最后执行 else。这里有一个新手非常容易搞错的思想要点elif 的条件判断是接力式的。当程序检查到score 80的时候其实隐含了一个前提——前面的score 90已经不成立了所以这个分数必然小于 90。因此elif score 80真正表达的语义是分数在 80 到 89 之间。如果你意识不到这一点将来写条件时容易画蛇添足比如写成elif score 80 and score 90。这样做虽然逻辑也没错但不必要——前面的 if 已经帮你筛掉大于等于 90 的情况了。有些初学者会问那我用多个独立的 if 行不行if score 90: print(优秀) if score 80: print(良好) if score 60: print(及格)行倒是行但结果完全不对。用户考了 95 分会连续输出优秀良好及格三行。因为每个 if 都是独立判断的互不干扰。记住一句话当你需要多条路只走一条的时候用 if-elif-else当你需要多个条件独立判断互不影响的时候才用多个 if。3. 循环的两种基本形态while 和 for3.1 为什么需要循环重复劳动交给机器写程序的人最讨厌的一件事就是重复。比如你想打印 1 到 100 的数字如果不用循环你得写 100 行 print 语句。这还只是 100如果要打印 1 到 10000 呢写到你怀疑人生。循环结构就是为了解决大量重复操作而生的。它的核心思想是让一段代码反复执行直到某个条件不再满足为止。Python 里有两套循环体系分别是 while 循环和 for 循环。初学者常见的困惑是不知道该用哪个其实只要搞清楚两者的适用场景就行。3.2 while 循环只要条件成立就一直做while 循环的结构跟 if 非常相似区别在于if 是条件成立就执行一次while 是条件成立就反复执行每次执行完回头看条件直到条件不成立为止。count 1 while count 5: print(count) count count 1这段代码的执行过程可以拆解为先把count设为 1检查count 5结果为 True进入循环体打印count也就是 1然后count变成 2回到第 2 步重新检查条件此时count 2依然 True继续循环……直到count变成 6此时count 5为 False循环终止这个检查条件-执行循环体-再检查条件的往复过程是理解 while 循环的关键。很多人学了很久都搞不清 while 的执行顺序其实就用四个字记先判后行。先判断条件再执行循环体。3.3 while 循环的死循环陷阱刚才那个例子每执行一次循环体count都会加 1。如果我把count count 1这一行去掉会发生什么count 1 while count 5: print(count)count永远是 1count 5永远为 True循环永远不会结束。这就是传说中的死循环。程序会一直打印 1直到你强制终止它。这一步不小心轻则程序卡死重则占用大量 CPU 资源。所以写 while 循环时一定要反复问自己一个问题循环体里有没有哪行代码在改变条件涉及的值如果一个 while 循环的循环体里没有任何改变判断条件的操作那这个循环十有八九是个死循环。不过死循环也不全是坏事。有些程序就是故意要一直运行下去的——比如聊天软件的服务器它需要 7x24 小时不间断地监听新消息。你看到一个程序卡住不动但它没有报错很可能就是故意设计成了死循环。3.4 for 循环沿着队列逐个处理for 循环跟 while 的思维方式不太一样。while 是只要条件成立就一直做for 是沿着一个序列把里面的元素逐个拿出来处理。它的语法是for 变量 in 序列: 循环体从序列里拿一个元素赋给变量执行一次循环体然后拿下一个元素再执行一次循环体直到序列里的元素全被拿完循环结束。举个最典型的例子遍历一个列表fruits [苹果, 香蕉, 橘子] for fruit in fruits: print(f我吃了一个{fruit})这个循环的执行过程是这样的第一次循环fruit 苹果打印我吃了一个苹果第二次fruit 香蕉打印我吃了一个香蕉第三次fruit 橘子打印我吃了一个橘子。序列到头了循环结束。注意for 循环里的那个变量名可以随便起但最好有意义。比如遍历列表students变量名就叫student遍历字符串里的每个字符变量名就叫char。好的命名让你写出来的代码像句子一样通顺。3.5 range 函数生成数字序列的利器for 循环最常见的搭档就是range()函数。这个函数能生成一个整数序列在配合 for 做重复固定次数的操作时特别好用。for i in range(5): print(第, i, 次循环)range(5)生成的是 0、1、2、3、4 这五个数。所以这个循环会执行 5 次但i的值是从 0 开始的。这个从 0 开始的现象让不少新手疑惑我想循环 5 次为什么不从 1 开始很简单因为 Python 的索引体系就是从 0 开始的。列表的第一个元素下标是 0第二个是 1以此类推。所以当你用range(5)去遍历一个长度为 5 的列表时lst [10, 20, 30, 40, 50] for i in range(5): print(lst[i])i是 0 时访问lst[0]也就是第一个元素i是 4 时访问lst[4]也就是最后一个元素。完美对应。range()还可以接收两个甚至三个参数range(1, 6)生成 1 到 5 的序列注意是左闭右开——包含左边的 1不包含右边的 6range(1, 10, 2)生成 1、3、5、7、9第三个参数是步长也就是每次跳几个数这个左闭右开的设计让很多人一开始不太适应但只要你记住一句口诀——顾头不顾尾基本就不会错了。range(a, b)生成的序列包含 a 但不包含 b。4. 分支和循环组合典型场景实战拆解4.1 场景一数字累加器先来一个入门必练的小项目计算 1 到 100 的所有整数之和。这个题目看着简单但第一次接触循环的人很少有能一遍写对的。total 0 for i in range(1, 101): total total i print(1到100的和是, total)我详细拆一下思路。首先我们需要一个变量total来装累加的结果初始值是 0。然后range(1, 101)会依次产生 1、2、3……一直到 100。每次循环total total i都把当前的i值累加到总量里。循环结束后total 就是 5050。这个累加器模式在真实开发中太常用了——统计订单总金额、计算购物车总价、汇总报表数据全都是这个套路先初始化一个变量然后在循环里不断更新它。4.2 场景二连续输入的成绩统计累加器只是单向操作如果循环里再嵌套分支判断威力就大不一样了。比如我们要统计一批成绩里有多少人及格、多少人不及格还要算出平均分。total_score 0 pass_count 0 n int(input(请输入学生人数)) for i in range(n): score int(input(f请输入第{i1}个学生的成绩)) total_score total_score score if score 60: pass_count pass_count 1 average total_score / n print(平均分是, average) print(及格人数, pass_count)你可以看到循环体内嵌了一个 if 判断。用户在循环里每次输入一个成绩先累加总分再判断是否及格并计数。这就是循环里套分支的典型结构。这里有个小细节值得注意f请输入第{i1}个学生的成绩中的{i1}是因为用户看到的编号是从 1 开始的而循环变量 i 是从 0 开始的。这种人对不上号的困扰非常常见养成自觉偏移的习惯能省很多事。4.3 场景三猜数字小游戏如果把前两个场景再升级一下加进 while 循环就能写一个交互感很强的猜数字小游戏了。这也是很多人最早感受到编程乐趣的程序之一。import random target random.randint(1, 100) guess -1 while guess ! target: guess int(input(请猜一个1到100之间的数字)) if guess target: print(猜大了) elif guess target: print(猜小了) else: print(恭喜猜中了)分析一下这个程序的执行过程。目标数字由random.randint(1, 100)随机生成。guess初始值为 -1保证一定能进入循环因为 -1 肯定不等于目标值。每次循环用户输入一个数字然后根据猜的数字和目标数字的大小关系走不同的分支大了、小了、中了。猜中时guess等于target下次回到 while 条件检查时条件为 False循环退出。这个例子非常巧妙地用到了上一节学的知识while 的先判后行、if-elif-else 的接力判断、以及变量在循环中不断被重新赋值的模式。5. 初学阶段高频踩坑实录缩进、死循环、边界值5.1 缩进错了逻辑全乱Python 里缩进不只是为了好看它就是语法本身。同一个代码块的语句必须有相同的缩进量而且通常约定用四个空格。最容易翻车的一种情况是if 条件本来只想控制一行结果下面跟着好几行你忘了哪些行该缩进。比如if score 60: print(及格) print(恭喜通过考试) print(成绩评估结束)这里print(恭喜通过考试)和print(及格)缩进相同都属于 if 的代码块。而print(成绩评估结束)没有缩进不在 if 里。如果你想让恭喜通过考试在不及格时不出现那这个写法就是对的如果你想让它在任何情况下都出现就得把它取消缩进。这种问题在代码量小的时候一眼就能看出来但在几百行的代码里缩进错误会让你排查到怀疑人生。我的建议是编辑器里打开显示空格/制表符的开关并且全程只用空格不用 Tab或者反过来但千万别混用。混用 Tab 和空格在 Python 3 里直接会报错这是规矩。5.2 死循环 缺少用户退出机制while 循环里还有一个非常典型的软死循环问题——就是程序不是在无限跑而是跑起来停不下来因为你没给用户提供退出途径。比如你写一个菜单系统while True: choice input(请输入选项) if choice 1: print(执行功能1) elif choice 0: print(退出成功) break注意这里的break关键字它的作用是强制跳出当前循环。这个程序用while True构造了一个永远为真的循环这样才能不断展示菜单让用户选择。而退出靠的是choice 0时触发的break。没有break或者任何其他退出条件这个菜单就永远出不去了用户只能强制关闭整个程序。所以凡是写while True这种无限循环一定要问自己循环体里有没有break出路在哪5.3 边界值循环次数永远比想象的多一次或少一次边界值问题是我在带新人时最常强调的也是考试、面试里最爱的出题点。回到range的左闭右开特性for i in range(1, 5): print(i)这个循环输出的是 1、2、3、4没有 5。很多初学者想当然地以为range(1, 5)包含 1 到 5结果发现少了一次最后一位就是取不到。我建议你在初次接触循环时养成一个习惯先用手推一遍小规模数据。比如range(1, 5)自己在草稿纸上写下 1、2、3、4再看程序输出是否一致。这种手动推算不仅能帮你理解执行过程还能在调试时快速定位逻辑错误。再给你一个实战中的例子。假设你想让程序打印 5 个星号写了for i in range(5): print(*, end)输出结果是五个星号。但如果你写成了range(1, 5)输出的就只有四个星号。这种差一个的问题在分数统计、报表生成、批量处理这些场景里通常很隐蔽——因为程序不报错只有最终结果跟预期对不上时你才会察觉。5.4 逻辑运算符的短路效应最后分享一个稍微进阶一点的细节但在分支判断里非常实用。Python 的逻辑运算符and和or有个特点——短路求值。什么意思当程序执行a and b时如果a已经是 False那么无论b是什么整个表达式必然是 FalsePython 就不会再去计算b了。同理a or b中如果a是 True整个表达式必然是 TruePython 不会去碰b。这个特性在真实项目里有个经典用法安全判断。if user ! None and user.is_vip(): print(欢迎VIP用户)如果user是 None表示用户不存在user ! None为 False后面的user.is_vip()根本不会被调用也就不会因为 user 不存在而报错。你要是把顺序写反了写成user.is_vip() and user ! None那么当 user 为 None 时程序会直接抛异常。这个把安全的判断放前面的小技巧关键时刻能帮你少写很多防御代码。6. 结课练习与下一步打算这一讲的内容到这里就接近尾声了。按照惯例留两道题供你自己练手巩固每一道都涵盖了分支和循环的核心知识点练习一让用户输入一段文本程序统计这段文本中字母、数字、空格以及其他字符各自的数量。提示你可以用 for 循环逐个遍历字符然后用if、elif、else做分类计数。练习二让用户猜一个随机生成的 1 到 100 之间的数但最多只允许猜 6 次。每猜一次程序提示大了小了或者猜中了超过 6 次还没猜中输出正确答案。提示需要用到计数器结合 while 循环和 break 来设计退出条件。我在实际带新人时发现很多人学到这个阶段会犯一个共同毛病急着去网上找代码抄。我的建议恰恰相反——不要看答案先自己动手写哪怕写错也比不写强。你现在犯的每一个缩进错误、每一条跑偏的逻辑都是在为将来排查复杂 bug 累积经验。等你有底气让上面两道题在不出错的情况下跑通就可以进入下一讲去学习循环的进阶玩法——嵌套循环、break 和 continue 的配合、以及更灵活的循环控制技巧那将是另一番天地。