程序这东西,很多初学者刚接触的时候都会有一个错觉:程序不就是从上到下、一行一行按顺序执行吗?这说的没错,但那只是最基础的顺序结构。真实世界里没有哪款软件是纯直线跑下来的——你点外卖时,程序要根据订单金额判断要不要收配送费;你登录账号时,程序要根据你输入的密码判断对不对;你刷短视频时,程序要根据你的点击行为决定给你推什么内容。这些"根据条件做不同处理"的能力,靠的就是分支和循环。没错,这一讲要聊的,就是编程里怎么让代码"长脑子"。
这一讲是系列教程的第三讲,前两讲应该已经把变量、数据类型、输入输出这些地基打好了。如果你还没掌握那些内容,建议先回去复习一下,因为今天讲的分支和循环,本质上就是把前面那些基础语法串起来的"骨架"。我会用大量贴近实际场景的例子,带你把 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 == 5 | True |
!= | 不相等 | 5 != 3 | True |
> | 大于 | 7 > 3 | True |
< | 小于 | 7 < 3 | False |
>= | 大于等于 | 5 >= 5 | True |
<= | 小于等于 | 5 <= 4 | False |
这里有个初学者最容易踩的坑:==和=是完全不同的东西。=是赋值,是"把右边的值装进左边的变量";==是比较,是"判断左右两边是否相等"。很多人一上来就把 if 语句写成if age = 18,然后程序直接报错或者出现诡异逻辑,问题就在这。
除了比较运算,还有一种生成布尔值的方式就是逻辑运算——and、or、not。这三个词的意思跟日常英语几乎一样:
a and b:a 和 b 同时为 True,结果才是 Truea or b:a 和 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 和 for
3.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永远是 1,count <= 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"请输入第{i+1}个学生的成绩:")) total_score = total_score + score if score >= 60: pass_count = pass_count + 1 average = total_score / n print("平均分是:", average) print("及格人数:", pass_count)你可以看到,循环体内嵌了一个 if 判断。用户在循环里每次输入一个成绩,先累加总分,再判断是否及格并计数。这就是循环里套分支的典型结构。
这里有个小细节值得注意:f"请输入第{i+1}个学生的成绩"中的{i+1}是因为用户看到的编号是从 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是什么,整个表达式必然是 False,Python 就不会再去计算b了。同理,a or b中如果a是 True,整个表达式必然是 True,Python 不会去碰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 的配合、以及更灵活的循环控制技巧,那将是另一番天地。