ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

董付国Python小屋61-70题复盘:核心考点与高频坑位解析

董付国Python小屋61-70题复盘:核心考点与高频坑位解析 刷完董付国老师的Python小屋编程题61-70我最大的感受是这些题表面上看是在考语法实际上是在逼你建立“用代码解决问题的思维框架”。作为国内Python教学圈里流传很广的一套练习Python小屋的题目一直以“知识点覆盖扎实、难度梯度合理”著称尤其是61到70这十道题恰好卡在基础语法刚讲完、面向对象还没正式深入的阶段非常适合用来检验自己对字符串处理、类型转换、列表操作、函数封装这些核心基本功的真实掌握程度。先说结论如果你现在正处于“能看懂参考答案但合上书自己写就卡壳”的阶段这组题是很好的试金石。我当年刷题时犯过不少蠢错误后来带学生做练习时又看着他们踩进同一个坑所以这篇博文不打算搬运题目和答案而是把这十道题背后的出题逻辑、必考知识点、常见错误和排查方法整理出来希望能给正在刷题的人一些真正有用的参考。1. 这十道题到底在考什么知识点分布与难度编排逻辑董老师的题目从来不是随手出的。我刷完61-70这组题后特意把所有涉及的知识点列了一张表发现出题思路非常清晰——这个阶段集中火力在四个方面字符串处理、数字与类型转换、列表/字典操作、函数封装。这四块恰好是Python基础语法学完之后、进入更复杂项目之前必须打牢的地基。1.1 为什么中间段位偏偏卡在这几个知识点上先看一张我整理的知识点分布表对照自己刷题时的直观感受核心考点常见出题形式容易栽跟头的地方字符串切片与格式化截取子串、反转、拼接、格式化输出切片边界写错、格式化符号用混类型转换字符串转数字、数字转字符串int()与float()的适用场景混淆列表增删改查与排序去重、排序、筛选、逆序排序时原列表被误改字典的键值操作统计频次、按值排序、键值互换访问不存在的键直接报错循环与条件组合多条件筛选、嵌套遍历循环内修改正在遍历的列表函数封装抽公共逻辑、返回多个值默认参数使用不当注意看这六类考点里没有一项是“偏难怪”的知识点。换言之这组题不是考你会不会某个冷门函数而是考你在写普通业务代码时最常用到的那些操作是否熟练。我自己的体会是刷到中间段位的练习题时题目本身的难度已经不体现在“懂不懂语法”上而是体现在“能不能在头脑中快速把需求翻译成代码结构”上。1.2 题目编排的隐性递进关系大多数人在刷题时只关注单道题的解法很少有人会去琢磨题与题之间的前后关系。我复盘时发现61-70这十道题的编排是有明显的递进意图的刚开始的题目往往只涉及一个知识点比如纯粹的字符串处理、单个列表排序目的是让你把单项操作练熟。中间的题目开始出现两个知识点的组合典型的是“字符串处理类型转换”或者“列表筛选排序”。到后面几道题则会要求你封装函数、返回多个结果这是从“会写一段代码”到“会设计一段代码”的过渡。理解这个递进逻辑很关键。你刷到后面不会做了不应该急着去翻答案而是应该倒回去想前面那些基础操作我是否真的完全掌握了以我的经验八成以上做不出来的情况根因都在前面的单项技能有漏洞而不是综合应用能力差。2. 字符串与数字处理几乎所有题目都绕不开的基本功不论61-70在你这套题里具体出现了什么排序、去重、还是统计类的题字符串处理都是绕不开的底层能力。而且这一块有个特点语法简单但细节极多每一个小坑都足以让你的程序直接报错或者输出错误结果。2.1 类型转换的边界int()、str()、float()真没那么随便用很多初学者觉得类型转换不就是int(“123”)或者str(456)嘛有什么好讲的。但实际做题时转换失败的场景才最考验人。先说最常见的坑int()只能处理“看起来像整数”的字符串。你写int(12.5)Python会直接抛ValueError因为“12.5”在int的眼里不是合法整数文本。同样float(abc)也会报错。所以凡是涉及“从用户输入或文件读取的数据”转成数字的题目第一件事永远是确认数据长什么样。还有一个容易被忽略的点布尔值其实是整数的一个子类。True 1、False 0在Python里是成立的。这就意味着如果题目里要求你统计某个字符串里满足条件的字符个数你随手写total (ch a)是能跑的——因为True会被当成1来累加。这种写法虽然能过但可读性极差我自己带练习时碰到学生写出这种代码都会建议改回显式的if判断。# 常见类型转换的正确姿势 s 123 num int(s) # 正常num 123 t 12.5 f float(t) # 正常f 12.5 # num2 int(t) # 报错ValueErrorint不能直接处理带小数点的字符串 # 安全转换的通用写法 def safe_int(value, default0): try: return int(value) except (ValueError, TypeError): return default2.2 切片操作步长和方向是最容易被忽略的细节字符串切片的格式是s[start:end:step]这个语法看着简单但一到具体题目里就洋相百出。最常见的错误有两个。第一个是边界混淆。Python切片是左闭右开区间也就是[start, end)end位置的字符是不包含的。很多人在切abcdefg想取前三个字符时写成s[1:3]取到的却是bc。第二个是步长为负时容易绕晕。s[::-1]是反转字符串这个技巧几乎每套练习题都会出现但很少有人去深究为什么它能反转——实际上是从末尾往开头逐个取。以我刷题的经验切片题做错之后最好的调节方式是把字符串写在一张草稿纸上标好每个字符的下标然后从左到右走一遍切片过程。一旦你真刀真枪地手动模拟过几次边界错误基本就能杜绝。s 0123456789 print(s[2:5]) # 输出 234注意不包含下标5 print(s[:5]) # 输出 01234start缺省从0开始 print(s[5:]) # 输出 56789end缺省到最后 print(s[::2]) # 输出 02468隔一个取一个 print(s[::-1]) # 输出 9876543210整个反转 print(s[7:2:-1]) # 输出 76543从下标7倒着取到下标3左开右闭反向时end不含2.3 格式化输出的三种方式实战中到底选哪种字符串格式化在练习题里出现频率极高考试题目经常要求输出特定格式的结果比如保留几位小数、对齐、补零。现在Python里主流有三种方式%格式化、str.format()、f-string。我个人的建议很直接新写的代码一律用f-string。原因很简单可读性最好变量直接写在字符串里不会出现格式串里变量顺序写错的低级问题。%格式化现在只在维护老代码时才会碰到str.format()虽然功能最全但绝大多数场景下都显得啰嗦。name 董老师 score 87.5 # 不推荐%格式化 print(姓名%s分数%.1f % (name, score)) # 一般般str.format print(姓名{}分数{:.1f}.format(name, score)) # 推荐f-string print(f姓名{name}分数{score:.1f})说一个实际刷题时的细节f-string里的大括号是特殊符号如果你需要输出一个大括号本身得写成{{}}。当初刷题时就见过一道让输出JSON样例的题好几个人卡在这里想不通为什么输出总缺大括号。2.4 字符串类题目的通用解题模板刷完这十道题我把字符串类题目的解题套路归纳成三步先想清楚输入是什么、需要切出什么、最终要拼成什么样。以常见的“统计字符串中各字符出现次数”为例标准思路是遍历字符串用字典做累计text python programming counter {} for ch in text: # 用get方法避免健不存在的报错 # 这里其实也用到了一点文件读取的思路 counter[ch] counter.get(ch, 0) 1 print(counter)这里有一个非常容易踩的坑Map和字典的键访问顺序问题。你直接写counter[ch] 1如果ch是第一次出现程序就会抛出KeyError因为字典里还没有这个键。正确的做法是先用get方法给定默认值或者用defaultdict。这类细节就是练习题想考察的——语法书里都写了但没写过代码的人到了真实场景就是想不起来。3. 列表操作与筛选从“会写循环”到“会选工具”刷到61-70这个阶段列表相关的题目难度通常不再停留在“增删改查”基础操作而是开始要求你做筛选、去重、排序、变换这类数据处理工作。可笑的是越是这样基础的操作越容易写出冗长且容易出错的代码。3.1 列表推导式一个式子代替三行循环很多初学者习惯用传统的for循环加append来构建新列表比如# 传统写法 result [] for x in numbers: if x % 2 0: result.append(x)上面的代码没问题但如果你刷题时发现自己每个列表操作都要写三到五行效率就明显低了。列表推导式可以把整段逻辑压缩成一行而且性能通常更好numbers [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result [x for x in numbers if x % 2 0]列表推导式的核心语法就三个部分输出表达式、遍历来源、过滤条件。初次接触的人可以把它们当成“for循环的一行版”来理解但刷题时必须刻意练习用它因为后面的题可能会在一个表达式里嵌套两个循环不熟练的话读都读不懂。3.2 排序与去重sorted的key参数是真正的分水岭先说排序。list.sort()是就地排序直接改原列表返回值是None而sorted()是排序并返回新列表原列表不变。这两者的区别几乎每套练习题都会考一次。在需要保留原数据的题目里用错sort结果就是原列表被悄无声息地改了后续代码全乱套。再说key参数。这是排序题里边最重要的内容。比如你想按字符串长度排序或者按字典的value排序都要靠key来指定words [python, java, c, javascript] # 按长度从短到长排序 words_sorted sorted(words, keylen) scores {张三: 88, 李四: 92, 王五: 78} # 按分数从高到低排序 ranked sorted(scores.items(), keylambda item: item[1], reverseTrue)对字典按值排序这个操作在练习题里出现的频率非常高。很多人第一次看到sorted(scores.items(), keylambda item: item[1])会有点懵其实拆开看很简单scores.items()返回的是(键, 值)元组的列表lambda item: item[1]就是告诉排序函数“拿每个元组的第二个元素当排序依据”。去重也有一个经典的坑。直接用set()能去重但顺序会乱。如果题目要求去重后保持原来的顺序正确做法是遍历原列表用一个新的集合记录已见过的元素遇到没见过的才加入结果列表data [3, 1, 2, 3, 4, 1, 5] seen set() unique [] for x in data: if x not in seen: unique.append(x) seen.add(x) # unique [3, 1, 2, 4, 5]3.3 map与filter什么时候用什么时候别用Python里map()和filter()是两个内置的高阶函数分别负责批量变换和批量筛选。在很多练习题参考答案里都有它们的影子。但我必须说句实话它们能做的一切列表推导式都能做而且通常更清晰。# map的效果等价于列表推导式 nums [1, 2, 3, 4] squared_map list(map(lambda x: x * x, nums)) squared_comp [x * x for x in nums] # filter的效果同样等价于列表推导式 evens_filter list(filter(lambda x: x % 2 0, nums)) evens_comp [x for x in nums if x % 2 0]我的建议是刷题阶段你应该以列表推导式为主把map/filter当作阅读理解能力来培养——因为别人的代码里会出现你得看得懂。但自己写的时候可读性优先。4. 函数封装练习题里最容易写出“一次性代码”的地方很多刷题的人有个坏习惯拿到题目把逻辑全部堆在脚本顶层从第一行写到最后一题能出结果就万事大吉。61-70这个阶段的题目已经开始有意识地考察模块化思维了典型标志就是部分题目会要求你“编写一个函数来实现xx功能”而不再是单纯输出结果。4.1 为什么要单独抽函数函数封装的意义在练习题阶段不容易被体会因为题目本身都很短你感觉不到重复代码的代价。但一旦你写了几个稍大一点的练习项目比如用Python处理一个成绩单文件或者做一个简单的文本统计工具就会发现同样的逻辑被复制粘贴了七八遍之后改一处漏三处苦不堪言。董老师这组题里涉及到函数的部分实际上是在给你打预防针把一段有明确输入和输出的逻辑单独抽出来取一个能说明用途的名字这就是所有工程化开发的基本单元。刷题时就算题目没有强制要求用函数我也建议你用函数来组织代码形成肌肉记忆。4.2 默认参数是可变对象时的惊天大坑每次讲函数参数我都必须专门拎出这个经典案例因为它几乎坑过每一个Python学习者def add_item(item, container[]): container.append(item) return container print(add_item(1)) # [1] print(add_item(2)) # [1, 2] —— 你预期的是[2]吗问题出在默认参数[]在函数定义时只创建一次后续所有调用都共享这同一个列表。要修复很简单默认值设为None函数内部再创建新列表def add_item(item, containerNone): if container is None: container [] container.append(item) return container这不是什么偏门知识而是Python官方文档里明确强调过的坑。但练习题的参考答案一般不会主动提这个细节所以我建议你把这条经验记下来——它不一定会出现在题目里但它会在你后面的每一次项目实践中出现。4.3 递归题的边界条件从李白打酒聊起网络热词列表里出现了“李白打酒python”这其实是一道非常经典的递归/回溯类编程题李白提着酒壶上街遇店加一倍遇花喝一斗最后问共有多少种可能的遇店和遇花顺序。这种题考察的核心就是递归的边界条件设计。递归问题我只有一个建议写递归函数时前两行永远是终止条件。递归之所以难不是因为调用自身这个动作难而是因为你必须清晰地知道“什么时候停止”。一旦递归没有终止条件程序就会无限调用直到栈溢出。# 以经典阶乘为例 def factorial(n): if n 1: # 终止条件最先写 return 1 return n * factorial(n - 1)刷到这十道题里有递归内容时务必养成的习惯是先用一个最简单的小参数比如n0或n1手动跑一遍递归过程确认终止条件没问题再上真正的测试数据。这个习惯帮我避免过至少十次“递归函数压根没走到终止条件”的尴尬情况。5. 做题中反复踩的五个高频坑和三条排查路径这部分是我最想分享的实战内容。我刷题时以及后来带学生时发现有些坑出现频率高得离谱——几乎每个人都会按顺序踩一遍。把这些坑单列出来比多刷二十道题还有用。5.1 五个经典高频坑先说总结再逐个展开。这五个坑分别是浅拷贝问题、循环内修改遍历对象、变量作用域混淆、input()拿到的一律是字符串、整数除法与浮点除法的混淆。第一坑列表的浅拷贝。很多题目要求你复制一个列表再处理直接写new_list old_list然后你改了new_list发现old_list也跟着变了。原因很简单这个等号只是把引用复制了一份两个变量指向同一块内存。正确做法是new_list old_list[:]或者new_list old_list.copy()。第二坑循环内删除正在遍历的列表元素。经典场景是“删除列表中所有偶数”nums [1, 2, 3, 4, 5, 6] for x in nums: if x % 2 0: nums.remove(x) # 你以为结果是[1,3,5]实际结果是[1,3,5,6]原因是在遍历过程中删除元素会让列表的索引整体前移循环漏掉了紧跟在被删元素后面的那一个。正确的思路是“新建列表保留要留的元素”或者倒序遍历再删除。第三坑变量作用域混淆。在函数里给一个全局变量赋值函数外部却看不到变化。不深入了解局部变量和全局变量之前这个现象会让很多初学者感到费解。如果确实需要在函数里修改全局变量需要用global声明。但更推荐的做法是函数通过参数传入、返回值传出尽量不要依赖全局变量。第四坑input()返回的永远是字符串。如果你直接拿input()的结果和数字比较比如if input() 18永远不成立。所有从input()拿到的数据必须按需转换成int或float再参与运算。第五坑/与//搞混。/是浮点除法结果永远是float//是整除直接去掉小数部分。练习题经常要求“计算平均数”“商和余数”这里最容易出问题。5.2 定位Bug的三条排查路径做题做错了不可怕可怕的是对着代码干瞪眼。我常用的排查路径有三条无论新手老手都适用第一条print大法。在关键位置打印中间结果。比如循环第几次时变量的值是什么排序后列表变成了什么。不要觉得print掉价它是排查逻辑错误最直接的手段。第二条二分定位。当一段代码很长时在中间位置加print看前面一半是否正常。如果正常说明问题出在后面一半如果不正常问题出在前面一半。这样递归缩小范围很快就能找到问题行。第三条边界值测试。用空列表、1个元素的列表、全是相同元素的列表、包含负数的列表去跑你的代码。很多隐藏Bug只在边界情况下暴露。5.3 验证答案对错的简单方法最后提供一个我在刷题时最常用的验证思路不要睁着眼看输出对不对要构造canonical测试用例。换句话说你自己心里先清楚“这个输入应该得到什么输出”再去跑程序验证。比如统计字符频次时用aaaa和abab这种结果明确的字符串做测试。提高刷题效率的方法不是做题的数量而是对每道题的测试意识——这个习惯你在以后做任何代码项目时都受用无穷。6. 从练习题到真实项目这十道题的落点在哪里有人觉得练习题跟真实开发是两码事刷完就忘。我的观点不太一样练习题里练的每一项都是真实项目中每天都在用的技能只是场景换了你没认出来而已。字符串切片和格式化在分析日志文件时无处不在。比如说你有一个日志文件每行格式是时间 - 级别 - 消息需要提取出所有ERROR级别的消息。这就是一道活生生的字符串分割题line.split( - )之后过滤。我这里也经常用Python处理文本数据字符串基本功扎实的人写出来的处理代码又短又稳基础不牢的人则会写出N层嵌套循环性能惨不忍睹。列表推导式、排序和筛选在数据分析预处理阶段的价值就不必多说了。拿到一份csv数据你要清洗、去重、排序、筛选Python的列表和字典操作就是最底层的工具。董老师在练习里让你们反复练这些反复练的正是数据处理的看家本领。函数封装是所有工程代码的起点。练习阶段养成了“把一个逻辑独立操作抽成函数”的习惯后面面对上千行代码时自然知道怎么拆分模块。我见过的很多转行学Python的人最大问题不是语法不会而是所有代码都堆在一起完全没有函数边界一旦出错就要从头读到底。这种代码别说别人看不懂他自己隔两天再看也看不懂。还有调试技巧。练习阶段的bug多半只是语法错误和逻辑小错真实项目的bug可能涉及数据异常、并发问题、环境差异但排查方法论是一样的先用print确认数据流到哪里开始不对再用二分法缩小范围最后做边界测试确认修好了没把别的地方弄坏。这套方法论没变过。我一直觉得董付国老师的Python小屋系列题目最好的地方在于它不追求题型花哨而是老老实实地把Python最核心的操作反复锤炼。61-70这十道题更是如此——单看每道题都不难但组合起来是在检验你对前一段学习的所有基础知识的综合运用能力。刷题这件事重要的不是分数和数量而是每道题做完之后你能否在另一道从未见过的新题面前认出它背后熟悉的套路。如果你刷这十道题的过程中有超过两道需要看答案才能写出来我的建议是先别急着往下刷回头把字符串、列表、函数这三块的基础代码各写一遍再回来做效果会好得多。
返回列表