1. 从一道课后题说起为什么第三章的上机实践值得单独拎出来聊如果你手头正在用那本《Python程序设计与算法基础教程第二版微课版》大概率已经翻到第三章了。这一章通常讲的是程序流程控制——顺序、选择、循环这三板斧外加几个经典算法入门案例。很多人学Python卡就卡在这一章前面变量、数据类型还能靠死记硬背混过去到了分支嵌套和循环嵌套脑子就开始打结了。我前后带过几批零基础的朋友入门Python也自己重刷过这本书的课后题。一个很明显的感受是第三章的上机实践题是整个入门阶段的分水岭。做出来了后面函数、文件操作、数据结构都能顺下去做不出来后面基本就是抄代码度日。所以这篇内容我不打算只丢几个答案给你而是把第三章上机实践里那些高频题型拆开讲清楚每道题背后的思路、常见坑以及我实际写的时候会怎么处理。这篇适合谁看一是正在上这门课、被上机实践卡住的学生二是自学Python、想找一套系统练习题巩固流程控制的初学者三是想拿这些题当教学素材的辅导者。我会按题型分类把选择结构、循环结构、循环嵌套、简单算法这几块的典型题都覆盖到每道题给出可直接运行的代码同时说明为什么这么写、还能怎么改。需要先说明一点不同印次、不同版本的教材课后题编号和具体数值可能有细微差异我下面讲的是第三章上机实践里最典型、出现频率最高的那几类题。你对照自己书上的题目把数值和条件换一下就能直接用。2. 第三章上机实践的整体题型分布与解题思路2.1 这一章到底在考什么第三章的核心就一句话让程序学会“做判断”和“重复干活”。教材一般会按这个顺序铺开——先讲if单分支再讲if-else双分支然后是多分支elif接着while循环、for循环最后是循环嵌套和break/continue。上机实践题基本就是围绕这些知识点设计的不会超纲太多。我把常见的上机题归成四类你对照一下自己的题目题型分类典型题目特征考察知识点分支判断类判断闰年、判断成绩等级、判断三角形能否构成if-elif-else、逻辑运算符单层循环类求1到n的和、求阶乘、判断素数while/for、累加累乘循环嵌套类打印九九乘法表、打印图形、找完数双层for、循环变量关系简单算法类百钱百鸡、斐波那契、水仙花数穷举、递推、数位拆分这四类题的难度是递进的。分支判断类只要逻辑清楚就能写单层循环类要注意循环变量的初始值和终止条件循环嵌套类是重灾区很多人写出来要么死循环要么结果不对简单算法类则需要一点“把数学问题翻译成代码”的能力。2.2 解题的通用套路我写这类题有个固定习惯分享给你先别急着敲代码拿张纸把三件事写清楚。第一输入是什么、输出是什么。比如“判断闰年”输入是一个年份整数输出是“是闰年”或“不是闰年”。这一步能帮你确定要不要用input、要不要类型转换。第二判断条件或循环条件用自然语言写出来。闰年的条件是“能被4整除但不能被100整除或者能被400整除”。写清楚了再翻译成代码基本不会错。第三手动跑一遍边界情况。比如判断闰年拿2000年、1900年、2024年各试一次看逻辑对不对。这一步能帮你提前发现逻辑漏洞比写完再调试省事得多。提示第三章的题90%的错误不是语法错误而是逻辑错误。语法错误Python解释器会直接告诉你哪一行有问题逻辑错误它不会报错只会默默给你一个错误答案。所以手动验证边界情况这个习惯越早养成越好。3. 分支判断类题目详解与代码实现3.1 判断闰年逻辑运算符的经典练习这题几乎是每本教材第三章的标配。题目要求输入一个年份判断它是不是闰年。闰年的判断规则是能被4整除但不能被100整除或者能被400整除。翻译成Python条件就是year int(input(请输入一个年份)) if (year % 4 0 and year % 100 ! 0) or (year % 400 0): print(f{year}是闰年) else: print(f{year}不是闰年)这段代码看着简单但有两个地方容易出错。一是运算符优先级and的优先级高于or所以括号其实可以省略但我建议你加上可读性更好也不容易记错。二是%取余运算year % 4 0表示能被4整除这个没问题但有人会写成year / 4 0那就完全错了/是除法结果是浮点数。我实测下来这题最常见的错误是把条件写成year % 4 0 or year % 400 0漏掉了“不能被100整除”这个限制。这样写的话1900年会被误判成闰年但1900年其实不是闰年。你可以拿1900、2000、2024这三个年份测一下就能看出区别。3.2 成绩等级转换多分支elif的典型用法题目一般是输入一个0到100的分数按区间输出等级。比如90分以上为A80到89为B70到79为C60到69为D60以下为E。score float(input(请输入成绩)) if score 0 or score 100: print(成绩输入有误) elif score 90: print(等级A) elif score 80: print(等级B) elif score 70: print(等级C) elif score 60: print(等级D) else: print(等级E)这题的关键在于elif的执行顺序。Python会从上到下依次判断一旦某个条件为真后面的分支就不再执行了。所以score 90必须放在最前面如果你把score 60放前面那90分也会被判定成D。另外注意我加了一个输入合法性检查score 0 or score 100放在最前面。教材上的题有时候不要求这个但实际写程序时用户输入什么都有可能加一层校验是基本习惯。你可以试试输入-5或者120看看不加校验会输出什么。3.3 判断三角形逻辑与边界条件的双重考验题目输入三条边的长度判断能否构成三角形如果能再判断是等边、等腰还是普通三角形。a float(input(请输入第一条边)) b float(input(请输入第二条边)) c float(input(请输入第三条边)) if a b c and a c b and b c a: if a b c: print(等边三角形) elif a b or a c or b c: print(等腰三角形) else: print(普通三角形) else: print(这三条边不能构成三角形)这题有两个层次外层判断能不能构成三角形内层判断是什么类型的三角形。构成三角形的条件是任意两边之和大于第三边三个条件必须同时满足所以用and连接。内层判断等边的时候a b c这种链式比较是Python特有的写法等价于a b and b c写起来更简洁。等腰的判断用or连接三种情况只要任意两边相等就行。注意浮点数比较相等是个坑。如果输入的是3.0和3在Python里3.0 3是True这个没问题。但如果经过一系列浮点运算得到的结果比如0.1 0.2 0.3结果是False。这题直接输入边长不涉及运算所以用没问题但你要知道这个坑的存在。4. 单层循环类题目详解与代码实现4.1 累加求和循环入门第一题题目输入一个正整数n求1到n的和。n int(input(请输入一个正整数)) total 0 for i in range(1, n 1): total i print(f1到{n}的和是{total})这题太经典了但我还是要说几个细节。range(1, n 1)生成的是1到n的整数序列注意是n 1不是n因为range的右边界是开区间。total i等价于total total i这是累加的固定写法。如果你想用while循环写也可以n int(input(请输入一个正整数)) total 0 i 1 while i n: total i i 1 print(f1到{n}的和是{total})while循环版本要手动维护i的自增忘了写i 1就会死循环。for循环版本更简洁不容易出错所以这类已知循环次数的题我一般优先用for。4.2 判断素数循环与break的配合题目输入一个正整数判断它是不是素数。素数的定义是只能被1和自身整除的大于1的整数。判断思路是从2开始到这个数的平方根为止看有没有能整除它的数。n int(input(请输入一个正整数)) if n 1: print(f{n}不是素数) else: is_prime True for i in range(2, int(n ** 0.5) 1): if n % i 0: is_prime False break if is_prime: print(f{n}是素数) else: print(f{n}不是素数)这题有几个关键点。第一n 1的情况要单独处理因为1不是素数负数和0也不是。第二循环范围到int(n ** 0.5) 1就够了不需要到n-1。为什么因为如果n有一个大于平方根的因子那必然有一个小于平方根的对应因子所以检查到平方根就足够了。这是数学上的优化能大幅减少循环次数。第三break的作用是一旦找到能整除的数就立刻跳出循环不用继续检查了。我见过有人把is_prime的初始值设成False然后找到能整除的数就设成True这是反的。初始应该假设它是素数找到反例再推翻。4.3 求阶乘累乘与循环边界题目输入一个正整数n求n的阶乘。n int(input(请输入一个正整数)) result 1 for i in range(1, n 1): result * i print(f{n}的阶乘是{result})累乘和累加几乎一样只是初始值不同。累加的初始值是0累乘的初始值必须是1因为0乘以任何数都是0。这个细节很多人第一次写会搞错。另外阶乘增长非常快n稍微大一点结果就会超出普通整数的范围。Python的整数是任意精度的所以不会溢出但计算会变慢。如果你输入1000结果会是一个非常大的数打印出来能刷屏。教材上的题一般n不会太大但你要知道这个特性。5. 循环嵌套类题目详解与代码实现5.1 打印九九乘法表双层循环的入门经典题目打印九九乘法表。for i in range(1, 10): for j in range(1, i 1): print(f{j}*{i}{i*j}, end\t) print()这题是循环嵌套的入门必做题。外层循环控制行数从1到9内层循环控制每行打印的列数从1到当前行号。end\t表示打印完不换行用制表符分隔内层循环结束后用一个空的print()换行。我见过最常见的错误是内层循环写成range(1, 10)这样每行都会打印9个式子变成完整的乘法表而不是三角形。还有人把i和j的位置搞反导致输出格式不对。如果你想打印上三角或者下三角的其他变体只需要调整内层循环的范围。比如打印下三角for i in range(1, 10): for j in range(i, 10): print(f{i}*{j}{i*j}, end\t) print()5.2 打印图形空格与星号的控制题目打印一个n行的直角三角形或者等腰三角形。直角三角形比较简单n int(input(请输入行数)) for i in range(1, n 1): print(* * i)等腰三角形需要控制空格和星号n int(input(请输入行数)) for i in range(1, n 1): print( * (n - i) * * (2 * i - 1))这题的关键是找出每行空格数和星号数与行号的关系。第i行有n - i个空格有2 * i - 1个星号。这个关系你可以手动列几行找规律第1行n-1个空格、1个星号第2行n-2个空格、3个星号以此类推。提示这类图形题我建议你先在纸上画出目标图形标出每行的空格数和符号数找到和行号的数学关系再写代码。直接凭感觉写很容易错。5.3 找完数循环嵌套与因子求和题目找出1000以内的所有完数。完数是指一个数恰好等于它的所有真因子之和比如6 1 2 3。for num in range(2, 1001): factor_sum 0 for i in range(1, num): if num % i 0: factor_sum i if factor_sum num: print(num)外层循环遍历2到1000的每个数内层循环找它的所有真因子并求和最后判断和是否等于原数。这题的计算量不小内层循环最多要跑999次外层1000次总共接近50万次判断。在现在的电脑上跑起来很快但你要知道这个时间复杂度是O(n²)。优化思路是内层循环只需要到num // 2因为一个数的真因子最大不会超过它的一半。这样能减少一半的计算量。再进一步可以只检查到平方根但求和的时候要把对应的另一个因子也加上逻辑会复杂一些。教材上的题一般用基础版本就够了。6. 简单算法类题目详解与代码实现6.1 百钱百鸡穷举法的经典案例题目公鸡5钱一只母鸡3钱一只小鸡1钱三只。用100钱买100只鸡问公鸡、母鸡、小鸡各多少只。for rooster in range(0, 21): for hen in range(0, 34): chick 100 - rooster - hen if chick 0 and 5 * rooster 3 * hen chick / 3 100: print(f公鸡{rooster}只母鸡{hen}只小鸡{chick}只)这题是穷举法的典型。公鸡最多买20只100/5母鸡最多买33只100/3所以外层循环范围可以缩小。小鸡的数量由总数减去公鸡和母鸡得到不需要再开一层循环这样就把三重循环优化成了两重。注意小鸡的价格是chick / 3因为1钱能买3只。这里用除法会得到浮点数和100比较时可能有精度问题。更稳妥的写法是5 * rooster 3 * hen chick / 3 100改成15 * rooster 9 * hen chick 300两边同乘3全部用整数运算。6.2 斐波那契数列递推思想入门题目输出斐波那契数列的前n项。斐波那契数列是1、1、2、3、5、8、13……从第三项开始每一项等于前两项之和。n int(input(请输入项数)) a, b 1, 1 for i in range(n): print(a, end ) a, b b, a b print()这段代码用了一个很Pythonic的写法a, b b, a b。这行代码同时完成了两件事把b的值赋给a把ab的值赋给b。因为右边先计算所以不会互相干扰。如果你分开写a b然后b a b第二行的a已经是新的b了结果就错了。这题也可以用列表来存n int(input(请输入项数)) fib [1, 1] for i in range(2, n): fib.append(fib[i-1] fib[i-2]) print(fib[:n])列表版本更直观但空间复杂度是O(n)变量版本只用了两个变量空间复杂度是O(1)。教材上的题一般两种写法都接受。6.3 水仙花数数位拆分的技巧题目输出所有的三位水仙花数。水仙花数是指一个三位数其各位数字的立方和等于该数本身比如153 1³ 5³ 3³。for num in range(100, 1000): hundreds num // 100 tens (num // 10) % 10 units num % 10 if hundreds ** 3 tens ** 3 units ** 3 num: print(num)这题的核心是数位拆分。百位用num // 100十位用(num // 10) % 10个位用num % 10。这个套路可以推广到任意位数的拆分。还有一种更简洁的写法把数字转成字符串再逐位处理for num in range(100, 1000): digits [int(d) for d in str(num)] if sum(d ** 3 for d in digits) num: print(num)这种写法更通用改成四位数、五位数只需要改range和指数。但考试的时候老师可能更希望你用整除和取余的方式因为那体现的是对整数运算的理解。7. 常见问题与排查技巧实录7.1 第三章上机实践的高频错误速查表我把这些年见过的、自己踩过的坑整理成一张表你写题的时候可以对照检查错误现象可能原因排查方法程序没有任何输出循环条件一开始就不成立检查range范围或while条件程序一直运行不结束死循环循环变量没更新检查while循环里有没有自增结果比预期多一个或少一个range边界搞错记住range左闭右开判断闰年结果不对逻辑运算符用错用1900和2000测试乘法表格式错乱内层循环范围不对检查内层range的终止值浮点数比较不相等精度问题改用整数运算或容差比较变量值不符合预期变量作用域或赋值顺序问题在关键位置加print调试7.2 调试逻辑错误的三个笨办法逻辑错误最麻烦因为Python不会报错。我常用的三个办法看着笨但很有效。第一个是加print。在循环里加一句print(fi{i}, total{total})把中间过程打出来一眼就能看出哪一步不对。调试完记得删掉或者注释掉。第二个是缩小规模。比如找1000以内的完数你先改成找20以内的手动算一下应该是6看程序输出对不对。规模小了手动验证容易问题也更容易暴露。第三个是手动模拟。拿一张纸把变量的值一步步写下来和程序的输出对比。这个方法慢但对于理解循环的执行过程特别有帮助。我刚开始学的时候每道循环题都手动模拟一遍后来就形成条件反射了。注意不要一上来就用调试器。对于第三章这种规模的题加print和手动模拟比调试器更快。调试器适合更复杂的项目简单题目杀鸡不用牛刀。7.3 代码写完之后的自检清单每次写完一道题我都会过一遍这个清单边界情况测了吗比如输入0、输入负数、输入1。循环的初始值和终止条件对吗会不会多一次或少一次变量名有没有写错Python不会提示未定义的变量名拼写错误只会报NameError。缩进对吗Python靠缩进划分代码块缩进错了逻辑就变了。有没有死循环的可能while循环尤其要注意。这个清单花不了两分钟但能帮你省下大量调试时间。8. 从会做题到会写代码几点个人体会第三章的题做完了答案对上了不代表你真的掌握了。我自己的经验是同一道题至少用两种方式写一遍收获会大很多。比如求和用for写一遍再用while写一遍判断素数用break写一遍再用标志变量写一遍。不同写法之间的差异能帮你更深刻地理解循环和分支的本质。另外别满足于“能跑就行”。代码能跑只是最低要求可读性、健壮性、效率都值得多想一步。比如输入校验教材上的题经常不要求但你加上去程序就更接近实际能用的状态。再比如循环范围能不能缩小一点能不能提前退出这些思考积累多了写代码的手感就出来了。最后说一个我踩过的坑不要背代码。第三章的题类型有限有人就把答案背下来考试的时候默写。但题目稍微变一下比如把求和改成求平方和把判断素数改成判断完数背的代码就用不上了。真正要记住的是解题的套路——怎么分析输入输出、怎么把自然语言条件翻译成代码、怎么验证边界情况。套路记住了题目怎么变都不怕。如果你正在做第三章的上机实践遇到卡住的题不妨先把题目里的数字换成小一点的手动算一遍把过程写下来再对照着写代码。这个笨办法比盯着屏幕发呆有用得多。