判断语句与循环语句:编程入门的核心逻辑与实战技巧
判断语句与循环语句基本是编程入门的第二道坎。第一道坎是变量和数据类型过了之后你会发现几乎所有程序都在做同一件事根据条件做选择然后一遍遍处理数据。day03这一天的内容就是把这两块掰开揉碎让新手不仅能看懂语法还能自己搭出带逻辑的程序。我习惯把判断和循环放在一起教因为它们在实际代码里从来不分开循环里要有判断来决定何时停止判断之后又常常要进入下一轮循环。这篇文章就是一份完全可跟着做的复盘笔记适合刚学完变量、想开始写有逻辑代码的人也适合带新人的同学做参考。1. 先从整体上拆解判断与循环到底在解决什么问题1.1 把选择翻译成机器能听懂的条件写程序最难的一点不是记住语法而是把一个现实问题翻译成“条件”和“动作”。生活中的选择很多是模糊的今天下雨就不出门那如果只是毛毛雨呢要不要带伞机器没法处理这种模糊它只认布尔值也就是真和假。判断语句要做的事就是把“如果满足某个条件就执行某段代码否则执行另一段代码”这个模型落地。最常见的判断结构是 if / elif / else。一个简单的现实案例是这样的假设程序要判断用户输入的成绩属于哪个等级逻辑就是“如果分数大于等于90记作A否则如果大于等于80记作B再否则如果大于等于70记作C其他情况记作D”。这里的核心并不是语法而是条件的划分方式。我见到不少新手喜欢把所有判断全用 if 写出来不用 elif结果程序性能倒无所谓但逻辑上很容易出现重复判断甚至互相冲突。elif 的作用是“一旦前面的条件成立后面的条件就不再看了”这样既节省无效判断也保证了每次只会走唯一的分支。判断语句真正考验人的地方在于边界条件。比如“大于等于”和“大于”的区别比如连续区间到底是从大到小判断还是从小到大判断。一个新手最常见的错误是把区间顺序写反导致后写的高分分支永远走不到。比如先判断“分数大于等于80是B”再判断“分数大于等于90是A”那么90分以上的人第一个条件就成立了直接掉进B分支。这在逻辑上完全说不通但如果你没有形成“从上到下逐条匹配”的执行模型就很难发现。1.2 循环不是“重复劳动”而是“批量处理”与“逐步逼近”很多初学者会把循环理解成“让一个动作重复很多次”这个说法不算错但很容易让人忽略循环的真正价值。循环最强大的地方是它可以对一组数据批量执行相同的处理逻辑也可以让程序反复逼近某个目标直到满足条件为止。比如统计一个班里所有学生的平均分、检查日志文件里的每一条记录、或者不断猜一个数直到猜中这些都离不开循环。循环的两种基本形态也要先分清。一种是“我知道要循环多少次”的固定次数循环典型场景是遍历1到100之间的所有整数或者处理列表里的每个元素另一种是“我不知道循环多少次但要满足某个条件才能停”的条件循环比如用户一直输入密码直到正确或者算法不断迭代直到误差小于某个阈值。搞清这两种场景之后代码选型就很自然固定次数、遍历数据用 for条件未知、需要持续处理用 while。循环里还有一个容易忽略的概念循环变量。for 循环里的计数器、while 循环里用来判断条件的变量都必须在每次循环中朝着“循环结束”的方向变化。否则就会陷入死循环程序一直跑下去不停止。我见过很多新手写 while 循环时忘了在循环体内部更新变量运行后界面直接卡死。要理解循环的执行过程最好在脑子里建立一个“循环三步走”的模型检查条件、执行循环体、回到条件检查。每一步都在改变某些状态直到条件不再成立。2. 核心细节解析与实操要点2.1 判断语句的写法、顺序与那些容易踩的坑先看一段最常见的成绩分级代码我会用它拆开讲判断语句的几个关键点score 85 if score 90: grade A elif score 80: grade B elif score 70: grade C else: grade D print(grade)这段代码能正常输出 B因为它严格按照从高到低的顺序判断。如果我把条件顺序改一下写成“先判断 score 80再判断 score 90”代码同样能运行但 90 分以上的学生也会被归到 B。这就是判断顺序的坑。判断区间类条件时最好遵循一个原则要么从小到大要么从大到小让每个分支覆盖一个连续区间并且最后一个分支一定用 else 兜底把所有意外情况吸收掉。还有一个高频坑等值判断。在 Python 里单个等号是赋值双等号才是比较。其他地方语言也一样只是在细节上略有不同。我记得一个学员排查了半天发现 if 语句里写成了单个等号导致变量被重新赋值条件永远是“真”程序表现非常诡异。这种问题初学者几乎每个人都会遇到而且代码不会报错只有结果不对。因此我在每一轮练习里都会强调写完判断语句第一件事检查所有比较符号。浮点数的比较也要格外小心。0.1 加 0.2 的结果在计算机里并不是精确的 0.3而是 0.30000000000000004。如果直接写if 0.1 0.2 0.3判断结果是 False。这个陷阱在金额计算、物理模拟里非常常见。处理办法是不要依赖浮点数的完全相等而是判断两个数之差的绝对值是否小于某个极小值比如abs(a - b) 1e-9。这不是代码风格问题而是计算机表示小数的方式决定的。多条件组合是另一个重点。Python 用and、or连接多个条件它们有一个非常实用的特性叫短路and左侧为假时右侧根本不会执行or左侧为真时右侧同样不会执行。利用短路可以保护代码不报错。比如判断一个用户是否能登录时可以写if user is not None and user.get(name) Alice:当 user 是 None 时后半个条件会被跳过程序不会因为对 None 调用方法而崩溃。这种写法在实际项目里能省掉大量判断嵌套。2.2 循环语句的三种形态和循环控制词编程语言里的循环形态虽然各有差异但逻辑上可以归为三类for 循环、while 循环、以及至少执行一次的 do-while 循环。Python 没有原生的 do-while但用 while 加 break 可以模拟出同样的效果。这三种形态的选型逻辑其实非常清晰我整理成一张表场景推荐写法特点遍历固定范围或集合for i in range(...) 或 for item in list不需要手动维护计数器天然清楚边界满足某个条件才执行次数不确定while condition灵活但必须自行保证条件最终会变为假至少执行一次再判断是否继续while True 配合 break适合菜单、输入交互、重试机制for 循环最常用的搭档是 range。很多新手搞不清楚range(10)和range(1, 10)的区别。前者的取值范围是 0 到 9共 10 个数后者是 1 到 9共 9 个数。range 的规则是“左闭右开”也就是包含起点不包含终点。这个规则如果不记住写列表索引、处理循环次数时就会频繁出错。我的建议是需要遍历列表下标时写成for i in range(len(items))需要取前 N 个数时写成for i in range(N)需要从 1 开始取 N 个数时写成for i in range(1, N 1)。最后一种里那个加一是新手最容易漏掉的地方。while 循环的写法看起来更简单陷阱却更多。while True是一个无穷循环必须搭配 break 才能退出。break 的作用是立即结束整个循环continue 则是跳过本次循环剩下的语句直接进入下一轮判断。两者的区别可以用一个收快递的场景来说明白break 是这单不收了直接回家continue 是这单有问题先跳过换个包裹继续收。在一些循环里continue 用不好会导致条件永远不更新同样造成死循环这一点我在后面的实战部分会详细说。循环还有一个小众但很实用的语法循环的 else 子句。在 Python 里for和while后面都可以跟else它的执行条件是循环正常结束没有被 break 中断。这个特性很适合用来做“查找不到就报错”的场景。比如在一个列表里找一个满足条件的元素如果找到了就 break此时 else 不执行如果遍历完全都没有找到循环正常结束else 就会执行并提示未找到。有人觉得这个语法反直觉但用熟之后,代码可以省掉一个标志变量。循环嵌套也值得注意。嵌套循环的执行顺序是先执行外层循环的一轮再把内层循环完整跑完然后回到外层循环继续下一轮。很多人在用嵌套循环打印矩阵时搞不清内层循环哪一维代表行哪一维代表列。这个没有统一答案取决于你的循环变量定义。关键是脑子里要有一个表格或矩阵的图像一层层对应上去而不是只看代码行数。3. 实操过程用“猜数字”把判断和循环彻底串起来3.1 第一版人猜数字把输入校验做扎实理论讲再多不如直接写一个能运行的小程序。我带的每次练习第一个综合题目都是猜数字程序随机生成一个 1 到 100 之间的整数用户输入猜测的数字程序提示“大了”或者“小了”直到用户猜中为止。这个题目麻雀虽小却同时用到了随机数、输入输出、判断、循环、break 和异常处理。先看第一版代码import random target random.randint(1, 100) count 0 while True: raw input(请输入1到100之间的整数) if not raw.isdigit(): print(输入无效请重新输入) continue guess int(raw) if guess 1 or guess 100: print(超出范围请重新输入) continue count 1 if guess target: print(小了) elif guess target: print(大了) else: print(f猜中了一共用了{count}次) break这段代码里最值得关注的不是猜数字本身而是对用户输入的防御。很多新手会直接写guess int(input(...))看起来干净但实际上如果用户输入了“abc”或者“12.5”程序会直接抛异常退出。我再加上了一层isdigit()判断保证只有纯数字才会继续走否则用 continue 回到循环开头重新输入。isdigit()只能判断非负整数所以后面还要检查范围。如果用户输入 200虽然它是纯数字但不在 1 到 100 之间一样要被拦下来。这个写法暴露了循环和 continue 的一个典型玩法利用 continue 跳过不合理的输入不会增加猜测次数。如果把 count 加一放在输入校验之前用户乱输几次就会被计入总次数体验很不好。正确的顺序是先校验再计数再比较大小。这个顺序也是我特别强调的“操作顺序要符合业务逻辑”的入门案例。程序运行的效果大致是这样的假设 target 是 73用户输入 50程序显示“小了”输入 80程序显示“大了”输入 73程序显示“猜中了”。整个过程里while True 一直保持循环直到内部 break 触发。分支判断放在循环体内每一次循环根据用户输入产生不同结果。这就是判断和循环协作的最直观样本。3.2 第二版让计算机猜你的数字顺手体验二分法第一版是人猜数字第二版我建议把它反过来让计算机来猜用户心里想的数字。这个版本更有意思因为它不仅用到判断和循环还能引出经典算法中的二分思想。规则是这样用户心里想好一个 1 到 100 之间的整数程序每次输出一个猜测值用户告诉程序这个猜测是“大了”、“小了”还是“猜中了”。low 1 high 100 count 0 print(心里想一个1到100之间的整数我来猜。) while low high: mid (low high) // 2 count 1 result input(f你心里想的是 {mid} 吗如果猜大了输入1猜小了输入2猜中输入0) if result 0: print(f我用了{count}次猜中) break elif result 1: high mid - 1 elif result 2: low mid 1这个程序的精妙之处在于它用两个变量 low 和 high 维护当前可能的数字范围每次猜中位数。这里中位数的计算要写成(low high) // 2用整数除法保证结果没有小数。如果不做整数除法mid 变成小数后面的 range 和比较都会乱套。我在实操中要求学生一笔一画在纸上写出 low、high、mid 的变化过程比如目标数字是 42 的时候第一轮 mid 是 50用户反馈“小了”high 不变low 变成 51第二轮 mid 是 75用户反馈“大了”low 不变high 变成 74这样范围一步步缩小最后必然收敛到目标值。有人会问猜大了之后为什么是high mid - 1而不是high mid因为当前这个 mid 已经明确被排除了不需要再留在可能范围内。如果写成high mid范围会一直包含一个不可能的值极端情况下会造成死循环。同样地猜小了之后要low mid 1。循环条件使用low high而不是low high是为了保证范围内只剩一个数字时还能进入循环被验证。用 1 到 100 这个区间最多只需要猜 7 次因为每次都能把剩余范围缩小一半2 的 7 次方是 128足够覆盖 100 种情况。这就是二分法的直观感受。第二版代码比第一版更能锻炼逻辑因为它的循环次数不是由固定的列表决定的而是靠不断更新 low 和 high 来逼近结束条件。如果漏写任何一个更新语句程序就会无限循环。写完这段代码后可以试着加入一个“作弊检测”功能如果用户既说小了、又说之前猜的数已经大于某个值这种矛盾输入可以被程序识别出来。这需要额外维护一个最小值和最大值不过对新手来说稍微有点难可以作为进阶练习。4. 常见问题与排查技巧实录4.1 死循环条件永远为真怎么办死循环是判断与循环练习里出现频率最高的问题现象是程序窗口卡住不输出任何内容或者疯狂刷屏。最常见的原因有三个while 后面的循环条件永远为真循环变量没有在循环体内部更新continue 写在了循环变量更新之前导致每次跳到开头时变量还是老样子。排查死循环最直接的办法是在循环体内部把关键变量的值打印出来看它到底有没有在变化。举个例子如果写while i 10:但循环体内部忘记写i 1那 i 永远是初始值条件永远成立。打印 i 之后你会看到每一轮输出的都是同一个数字瞬间就能定位问题。另一个容易被忽略的场景是在循环里用了 continue但 continue 跳过了变量更新语句。比如循环体是先输入数据再更新计数器但输入校验失败时直接 continue 回到循环开头计数器就不会增加。解决办法是把更新操作放在 continue 之前或者改用 break 加标志位。用纸笔追踪变量也是一个好方法我甚至觉得这是比打印更快的方式。4.2 循环次数差一个range的边界到底怎么算少循环一次或者多循环一次是遍历型任务里最典型的问题业内叫 off-by-one。这个问题的根源几乎都出在 range 的边界规则上。range(1, 5)产生的是 1、2、3、4共 4 个数字而不是 1 到 5 共 5 个数字。因为 range 是左闭右开不包含终点。很多新手写for i in range(1, 100)想遍历 1 到 100结果少算了一个数字 100。正确的写法是range(1, 101)。我提供两个排查方法。第一种是打印中间量在循环第一行输出当前循环变量的值观察它到底是从哪里开始到哪里结束。第二种是直接算长度用len(list(range(1, 101)))这个临时打印确认一下元素个数。处理数组下标时也要注意长度为 n 的列表最后一个元素的下标是 n-1不是 n。如果遇到列表越界优先怀疑是不是自己在循环范围里写多了。4.3 判断条件顺序导致的逻辑Bug条件顺序错误是判断语句里最难发现的 bug因为程序不报错结果却不符合预期。前面提到过成绩分级的例子这类问题的共同点是前面的条件覆盖范围太宽把后面的分支全部遮蔽了。比如判断一个年份是不是闰年正确逻辑是先判断能不能被 400 整除其次判断能不能被 4 整除但不能被 100 整除。如果先判断能不能被 4 整除那么能被 400 整除的年份当然也被 4 整除后面的分支就不会执行闰年判定就会漏掉特殊情况。排查这类问题靠肉眼盯代码是很难的。我习惯用这样的办法设计一批测试用例尽量覆盖每个分支的边界值。比如成绩分级至少要用 59、60、70、79、80、89、90、100 这些点去验证。如果某个输入走到的分支和预期不一致就把条件从上到下逐个注释或者打印每个条件的布尔值。判断语句的调试本质上是在做逻辑推理只要把“程序如何逐行匹配”这个流程在脑子里跑清楚问题很快就能浮出来。4.4 输入异常与循环结合try/except其实很简单实战中用户输入是永远不可控的因此输入异常处理几乎是必考内容。isdigit()只能解决整数输入的问题如果代码要处理浮点数比如输入 3.14isdigit()会直接返回 False导致合法输入被拒绝。这时更好的方案是用 try/except 包装类型转换让程序尝试把字符串转成目标类型转失败就进入异常分支。while True: raw input(请输入一个数字) try: num float(raw) break except ValueError: print(输入不合法请重新输入)这段代码的思路是先尝试转换如果成功就退出循环如果抛出 ValueError就打印提示并继续循环。注意 break 的位置它只在转换成功后执行这样保证循环结束时 num 一定是一个有效数字。try/except 不是专门给新手准备的进阶技巧而是处理输入问题时绕不开的基础能力。我见过不少项目在入口处没有做任何异常捕获用户一旦输错整个程序直接崩溃日志里却看不出任何有用信息。把异常处理和循环配合起来是最务实的防御式编程。如果只让我给一个建议我会说判断和循环这两个知识点千万不要分开去背语法一定要放进一个完整的交互程序里去练。我最常用的练习就是猜数字因为它足够小却能把随机数、输入校验、多种判断、循环控制、边界更新全部串在一起。你可以在第一版和第二版的基础上继续扩展比如统计用户猜数字的最优次数、加入“游戏结束是否再来一局”的选项、或者把二分法改成更通用的查找逻辑。每一次扩展都是在逼自己重新审视条件和循环的边界。我每次带新人走完这个流程他们对“程序是一步步按顺序执行”这个底层认知都会明显变得更扎实。