这几道题折腾了我两个晚上说实话刷《Python语言程序设计基础》第3章的练习题最难受的不是不会写而是“看答案觉得很简单自己一写就报错”。嵩天老师的教材有个特点例题讲得很透但练习题偏偏不给你完整代码只给你思路提示。这就逼着你自己去补中间的坑。这篇文章把我整理出来的第3章程序部分练习题的解题思路和参考答案整理成了一份笔记同时把做题过程中容易踩的坑和背后的原理一并讲清楚让正在啃这本书的读者不用再一个报错一个报错地试。1. 第3章练习到底在练什么1.1 这一章的定位从“语法”跨到“思维”学Python的人经常有一个误区觉得语法看完就等于会编程了。但真正开始做嵩天老师第3章的练习题时会发现事情没那么简单。第3章主要讲的是基本数据类型、字符串、数字运算和格式化输出配套的练习题表面上看是在考语法实际上是在逼你完成一次思维转换把数学公式或者日常需求拆解成Python能一步步执行的逻辑。举个例子书里要求你输入一个三位数然后输出每个数字之和。这道题看起来只是考除法和取余但它真正的用意是让你理解整数拆解的算法模型。很多人一开始会写a int(input())然后直接a / 100结果输出一堆小数完全对不上。这个问题不是粗心而是没有建立“整除//和取余%是拆解整数的一对工具”这个意识。所以我的建议是做第3章练习之前先把两类东西封存在脑子里。第一是数据类型之间的转换规则第二是格式化输出必须精确到客户需要的格式。这两类问题在练习题里占比最高也最能拉开差距。1.2 做题之前先搞明白的四件事我把第3章习题里共通的基本功整理成了四个判断标准每一道题做之前都先拿这四个标准过一遍会少踩很多坑。输入是什么类型input()拿到的一定是字符串你要自己决定是否转成int或float。输出格式有什么要求保留几位小数、要不要补零、数字之间用什么分隔符这些必须在写代码之前想好。有没有边界条件比如输入0、输入负数、输入空字符串程序会不会崩。算法能不能在纸上推演一遍哪怕是三位数求和这种简单题目先用数学式子算一遍再翻译成代码准确率会大幅提升。这四件事不一定每道题都涉及但绝大部分练习题都能套进去。我把它们当成第3章的通用方法论后续解题过程也基本按照这个顺序来推。1.3 关于“参考答案”的说明教材最后只给结果不过程序网上能找到的版本又良莠不齐。我自己在做的时候把同一道题尝试了两种甚至三种写法最后整理出来的这套答案未必是唯一的但至少保证思路清晰、代码能跑、输出符合常规预期。更关键的是我会顺便把错误版本和错误原因写出来这比贴一个标准答案有用得多。2. 数字计算与类型转换类习题解析2.1 经典题三位正整数各位数字之和先看一道最典型的题目“从键盘输入一个三位正整数计算并输出各位数字之和。”这道题我在不同版本的书上见过很多次它考察的就是整除与取余的组合使用。快速看一眼正确解法num int(input(请输入一个三位正整数: )) hundreds num // 100 tens (num // 10) % 10 ones num % 10 total hundreds tens ones print(total)这里最关键的是百位的计算。num // 100取的是整数商比如543 // 100 5十位是num // 10得到54再对10取余得到4个位直接对10取余得到3。每一步我把中间结果都打印出来验证过这样对初学者来说最直观。我之前见过有人这样写h int(num / 100)。虽然运行结果一样但这并不是好习惯。因为/是浮点除543 / 100 5.43再int()强行截断成5看起来没问题但如果数字有浮点数参与运算容易出现精度误差。而//是地板除专门用于整数运算在Python里两者的边界非常明确。我的建议是在整数拆解的算法里统一用//和%没必要混用/。另外别忽略输入格式。如果你直接写num int(input())用户一旦输入带小数点的内容就会报错。好的程序应该提示用户“请输入三位正整数”并且在讲解时强调input()返回的是字符串int()转换只能处理数字字符串中间的转换过程本身就是这道题的一部分。2.2 浮点数格式化输出保留两位小数和百分号第3章里另一类高频题是格式化输出。题目大概长这样“输入一个浮点数分别输出它保留两位小数的形式、保留两位小数的百分数形式以及科学计数法形式。”这道题不只是考察round()函数而是考察你是否掌握了字符串格式化的三种写法。先给出一版稳定解法x eval(input(请输入一个浮点数: )) print(原值: {:.2f}.format(x)) print(百分数: {:.2%}.format(x)) print(科学计数法: {:.2e}.format(x))这里我用了eval()而不是float()是因为有些同学习惯输入3.14有些会输1/3。eval()能把表达式算出来但安全上是有隐患的正常练习用没问题正式开发不建议。如果输入的是3.14159三种输出结果分别会是3.14、314.16%、3.14e00。容易犯错的地方是百分数的格式。很多人以为要自己先乘100再拼一个%符号就像print(str(x * 100) %)。这样做虽然结果对但代码笨重而且位数控制非常麻烦。{:.2%}是直接把浮点数当作小数处理自动乘100并追加百分号是格式化里的内置特性效率和可读性都更好。还有一个细节{:.2f}是“四舍六入五成双”还是常规四舍五入Python的format采用银行家舍入法跟数学课上的四舍五入略有差别。比如format(2.675, .2f)会输出2.67而不是2.68这是由浮点数的二进制存储决定的。考试中如果遇到精确舍入要求建议用decimal模块但这属于进阶内容普通练习里只需要知道这个现象就够了。2.3 注意运算顺序与类型陷阱数字计算这一类题里有个经典陷阱是运算符优先级。请看下面这行代码total num // 100 (num // 10) % 10 num % 10如果当初你没加括号写作num // 10 % 10也没问题因为//和%的优先级相同从左往右算就是先整除再取余。但有些同学会把十位写成num // 10 % 10 num // 100 % 10看似差不多其实结果会错位。为降低认知负担我建议任何混合运算都显式加括号哪怕你知道优先级别人看代码也能一眼看懂。另外类型转换上有一个常犯的错把字符串和数字直接拼接。比如你要输出“计算结果为15”如果写成print(计算结果为 total)Python会直接抛TypeError。因为字符串不能和整数用拼接必须先把total转成字符串或者直接用格式化语法。这个错误几乎每个初学者都会遇到解决办法就是养成习惯涉及输出的地方要么写print(结果是:, total)要么用print(f结果是{total})别手动拼字符串。3. 字符串处理类习题解析3.1 逆序输出与切片规则字符串处理在第3章里占了很大的比重其中出现频率最高的是逆序输出。“输入一个字符串将其逆序输出”看起来简单却涉及Python切片中的步长概念。正确写法s input(请输入一个字符串: ) print(s[::-1])这三个字符[::-1]看起来很玄其实拆开就清晰了第一个冒号前是起始位置默认0第二个冒号后是结束位置默认末尾第三个-1是步长表示从右往左一个一个取。如果你写成s[:-1]那只是把最后一个字符去掉跟逆序完全不是一回事。还有一种常见题目是“输出字符串中的奇数位字符”或“每隔一个字符取一个”。这种题要用步长为2的切片实现s input(请输入一个字符串: ) print(s[::2]) # 从0开始隔一个取一个 print(s[1::2]) # 从1开始也就是偶数位置从0计很多教材讲到切片会画图但我个人更推荐用一个简单实验来记忆先定义一个短字符串比如abcde然后在交互式环境里试s[:]、s[::]、s[::-1]、s[1:4]把每次输出打印出来。用不了两分钟就能把索引规则彻底搞懂比死记硬背强得多。3.2 字符串字符统计与遍历另一类必练题目是字符统计。“输入一行字符串统计其中大写字母、小写字母、数字和其他字符的个数。”这类题的核心是学会用for循环遍历字符串里的每一个字符并对字符类型做判断。答案如下s input(请输入一个字符串: ) upper lower digit other 0 for ch in s: if ch.isupper(): upper 1 elif ch.islower(): lower 1 elif ch.isdigit(): digit 1 else: other 1 print(大写字母:, upper, 小写字母:, lower, 数字:, digit, 其他:, other)这题大家容易掉进去的坑有几个。第一个是忘记字符串是不可变对象试图用ch ch.upper()去修改原字符串。这里不需要修改只需要把每个字符逐个检查。第二个是混淆isdigit()和isnumeric()前者只判断0-9以及部分unicode数字字符范围更窄中文“一”用isdigit()判断是False而isnumeric()是True。第三个是忘记处理空格空格会进入else分支归到“其他”中这在统计里符合预期。如果题目要求忽略大小写做统计可以用s.lower()先把字符串统一转成小写然后再遍历。记住lower()返回的是新字符串不会改变原变量所以你要把返回值赋给一个新变量再使用。3.3 format与f-string的实用细节字符串格式化是第3章的暗线几乎每道练习题的最后输出环节都会用到。我强烈建议在练习阶段就统一使用f-string或者format而不是拿去拼字符串。f-string的基本用法name Python score 95.5 print(f{name}的成绩是{score:.1f}分)注意f-string在Python 3.6之后才支持如果你的运行环境是Python 3.5及以下就要用format()。教材可能还在教format()但实际工作中f-string更常见所以两种都要认识。这里有一个很实用的扩展format指定宽度和填充符。比如输出“学号”对齐的表格时for i in range(1, 4): print(f第{i:03}号)输出会是“第001号”、“第002号”、“第003号”。03表示总宽度3用0在左边填充。这类写法在生成文件名、格式化时间时极其常用练习题里虽然不直接考但能帮你理解格式化机制的底层逻辑。另外format还支持千分位分隔符比如f{1000000:,}输出1,000,000。第3章练习很少考到格式化输出复杂的嵌套但如果你能掌握上面这些做后面的数据处理章节会轻松非常多。4. 综合应用题从公式到程序的完整推演4.1 用级数求π的近似值第3章练习里有一道比较有代表性的综合题“利用公式π/4 ≈ 1 - 1/3 1/5 - 1/7 ... 计算π的近似值直到最后一项的绝对值小于1e-6。”这题综合了循环、条件判断、符号切换和浮点精度处理是第3章从“语法”转向“算法”的桥梁。一个通用解法如下sign 1 denominator 1 total 0.0 item 1.0 while abs(item) 1e-6: total item denominator 2 sign -sign item sign / denominator pi total * 4 print(pi , pi)这里需要重点解释的是循环的退出条件。每一次循环先把item加到total中再更新denominator和sign然后计算下一个item再回到while判断条件。如果把item初始值设为1那么第一轮加进去的就是1第二轮是-1/3这样通过不断交替正负号就实现了公式的累加过程。我踩过一个坑在循环里把分母更新语句放错了位置。如果把denominator 2放在item sign / denominator之后那么分母永远不会更新程序会死循环或者结果不变。初学者遇到这种情况建议用一个计数器比如设置一个max_iterations限制最多循环十万次防止无限循环把电脑卡住。虽然这题理论上会收敛但程序里加一个保险丝能有效防止低级错误。另外当你把这个π值和math.pi对比时会发现前几位一样但后面有细微差距。这是正常的因为级数收敛到精确π需要更多项1e-6的精度只能保证前5位左右一致。教材这道题的目的是让你理解“用迭代逼近一个数值”的思想而不是让你得到精确π。4.2 简单计算器分支判断与类型转换还有一种常见的综合题是“模拟简单计算器输入两个数和一个运算符输出结果”。题目大意比如输入3 5输出8.0输入10 / 4输出2.5。这不只是把三个值输入然后算一遍还要考虑除数为0、非法运算符等边界。参考代码a float(input(请输入第一个数: )) op input(请输入运算符( - * /): ) b float(input(请输入第二个数: )) if op : result a b elif op -: result a - b elif op *: result a * b elif op /: if b 0: print(除数不能为0) else: result a / b else: print(无效运算符) if op in [, -, *, /] and not (op / and b 0): if result.is_integer(): print(结果: {:.0f}.format(result)) else: print(结果:, result)这里有一个练习时容易忽略的点float(input())之后如果用户输入3Python会存成3.0那么3.0 5.0 8.0。很多同学希望整数结果输出成8而不是8.0就需要用is_integer()来判断。这个知识点在第3章正文里不一定重点讲但练习题会用到属于题后补充技能。我之前处理这个问题时直接用print(结果:, result)结果输出8.0老师说严格不符合题目预期于是我又用int(result)强转但这样遇到3.5就会出错。is_integer()才是稳妥方案。此外注意elif结构。如果你用了四个独立的if那么在输入非法运算符时它可能把前面的分支也判断一遍即使逻辑结果相同效率更低。写分支时优先用elif串联并能保证互斥。4.3 温度换算类换汤不换药的固定套路教材前面的章节大概率出现过摄氏度和华氏度的换算。第3章练习里通常会升级为“输入一个带符号的温度值例如100C或212F自动转换并输出另一种单位”这样的题目。它结合了字符串切片、类型转换和分支判断难度适中但综合性很强。参考解法temp input(请输入带单位的温度(例如100C或212F): ) if temp[-1] in [C, c]: f eval(temp[0:-1]) * 9 / 5 32 print(转换后的温度: {:.2f}F.format(f)) elif temp[-1] in [F, f]: c (eval(temp[0:-1]) - 32) * 5 / 9 print(转换后的温度: {:.2f}C.format(c)) else: print(输入格式错误)这题的灵魂在temp[0:-1]。它从字符串中取出除了最后一个字符之外的所有数字部分再交给eval或float转换成数值。如果你用int(temp[0:-1])输入98.6F时就会抛异常因为int()不能解析小数。更好的写法是float(temp[0:-1])这样能同时兼容100和98.6。还有一个隐藏细节temp[-1]取的是最后一个字符如果用户输入末尾带空格比如100C 就会进入else分支提示格式错误。这不是代码问题而是输入规范问题。实际开发中可以用temp temp.strip()先去空格但在练习里要求用户严格按格式输入也是合理的。这类题的公共套路可以概括为三步先确定标识位置再切割数字部分最后转换计算。温度换算、货币换算、成绩等级计算都是这个模板换汤不换药。5. 高频报错与排查实录5.1 缩进和冒号第3章出现最多的报错原因写第3章练习时新手的报错很大比例来自缩进和冒号。Python用缩进表示代码块if、for、while、def的下一行必须缩进。很多同学在交互环境里复制代码时缩进会变成空格和Tab混用编辑器直接报IndentationError: unexpected indent。我的建议是把编辑器设置里的Tab自动替换成4个空格或者干脆全部使用空格缩进。这跟你用什么系统无关纯粹是编码习惯。还有一种是“忘记冒号”。if、for、while、def语句行的末尾一定要有英文冒号。中文输入法全角冒号和英文半角冒号长得不一样但Python只认英文冒号。如果你遇到SyntaxError: invalid syntax优先检查上一行是不是把:写成了。这是个非常低级的坑但几乎每个人都踩过。5.2 eval和float的取舍很多标准答案喜欢用eval(input())因为省事数字或表达式都能处理。但eval()的本质是把字符串当成Python代码执行如果你输入__import__(os).system(ls)它真的会执行系统命令。做练习问题不大但在实际项目里这属于严重安全隐患。教材本身为了教学方便用了很多eval()但我个人建议处理数字输入时优先用float()或int()这样程序更安全也不会出现用户输入3 5时直接计算出8的情况有时候这反而不符合出题意图。如果确实需要用eval也要限定输入内容。你可以先判断字符串是否只包含数字、小数点和运算符再交给eval()执行这样能降低一部分风险。不过对第3章练习来说直接记住“普通输入用float要用表达式才用eval”这个原则就够了。5.3 浮点数精度导致的结果偏差很多同学在计算温度换算时明明公式对结果却出现100.00000000000001这样的数字。这不是你算错了而是浮点数二进制存储的固有问题。第3章练习通常不会让你处理这种精度细节但你需要形成共识凡是要输出给人看的结果一律用格式化输出控制小数位数。不要依赖print的默认精度它只会忠实地展示内存里的值。如果在调试时发现了精度问题可以用round(result, 2)临时查看但注意round()返回的是浮点数如果你接着做运算仍然可能出现新误差。真正的解决办法是用Decimal或者格式化成字符串后再输出。这个知识点在后面网络爬虫和数据分析的章节会反复出现。5.4 字符串索引越界与空输入最后记录一个高频异常IndexError: string index out of range。这个错误经常出现在需要取字符串首尾字符的题目中。例如上面温度转换题如果用户直接按回车不输入内容temp[-1]就会越界。解决办法是在取索引之前先判断字符串长度if len(temp) 0: ...还有一个相关的坑把用户输入的字符串直接用于切片比如temp[0:-1]要求字符串至少长度为2。如果用户只输入C切片得到空字符串float()会报错。所以综合题里建议先判断长度再判断单位字符最后才做数值转换顺序不能反。我见过很多同学的逻辑是先转换再判断单位结果单字符输入直接让程序崩溃。练习的时候养成先校验再计算的习惯对后面做工程项目非常有帮助。6. 解题以外的几点体会整理完这些习题我最深的感受是第3章的练习题并不是为了为难你而是逼着你在“读代码”和“写代码”之间建立一座桥。很多人卡在某一题上并不是因为语法不会而是因为没掌握拆解问题的顺序。比如拿到“温度转换”这一题你先别急着写代码先在纸上写下输入是什么、输出是什么、中间有几步然后再翻译成Python。这个习惯一旦养成之后学函数、学类、学文件操作都会顺利很多。还有一个我自己的体会是“先跑通再优化”。做练习题时第一版代码通常又长又笨比如用一堆if处理输入格式这不丢人。先让程序在任何输入下都不崩溃再去考虑用更巧妙的列表推导、字典映射来简化代码。用第3章的知识能解决的题目就先用第3章的知识解决硬套后面的高级语法反而会让思路混乱。最后再分享一个小技巧书里每个练习题的输出格式往往藏有玄机。有的要求空格分隔有的要求逗号分隔有的要求保留固定小数位。提交之前一定要把输出示例和你的输出逐字符比对一遍。我遇到过不少次“代码正确但输出格式多了一个空格”导致的报错这种题目不是考算法是考细心。能把第3章的题做到“一次过”说明你真的开始像一个程序员那样思考了。