ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python经典练手题100道:从基础语法到综合实战的分层刷题指南

Python经典练手题100道:从基础语法到综合实战的分层刷题指南 打开浏览器收藏夹搜“Python练手题”你大概率会看到几十个标题带“100道”“全套”“附答案”的页面收藏之后基本吃灰。这不是你的问题是这类资源普遍没解决一个核心问题题目够全但缺少一条让你真正刷下去的主线。今天想聊的这套“100道Python经典练手题”我把它从头到尾过了一遍也用在了带新人、自己复习语法、准备面试三个场景里。结论先说这不是一本“刷完就忘”的题集它的价值在于——题目按语言基础、数据结构、算法思维、综合实战四个层次排列每一道题都能对应到你平时写脚本、处理数据、写接口时的真实需求。更实际一点说这套题最适合两类人一类是刚学完Python基础语法、想做点东西又不知道从哪开始的初学者另一类是写业务代码写久了、想系统补一下基本功的开发者。题量100道摆在那里看着吓人但拆开看很多题30秒就能写完只有少数需要你静下来画图推演。下面我会用几个章节把这个题库的用法、重点题型、常见坑点和刷题节奏拆开聊尽量让这份资源真正被用起来而不是继续躺在收藏夹里。1. 这套练手题到底在练什么设计思路与整体拆解1.1 为什么是100道而不是10道或500道先解释第一个问题为什么题量是100道。说实话10道题太少覆盖不了Python语法中那些“不常用但很容易考”的细节点比如装饰器、生成器、lambda配合map/filter这些在普通业务代码里一个月都用不了一次500道又太多普通人按每天5道算要刷3个多月中间一旦断档前面做过的思路全忘光。100道是一个刚好能形成“刻意练习”密度的数字——每天5道20天一轮每天3道一个月出头一轮。这个周期落在人脑记忆曲线的遗忘临界点之前第一轮刷完第二轮巩固时你会发现很多题“有印象但写不全”这种状态恰恰是复习效率最高的时候。这套题内部也分了梯度不是100道题平铺在那里。我数了一下大致可以分为四层第一层是基础语法题大概30道覆盖变量、循环、条件、字符串、列表、字典、集合这些最常用类型第二层是函数和模块题大概25道覆盖函数定义、参数传递、递归、lambda、内置高阶函数第三层是数据结构和算法入门题大概30道覆盖排序、查找、指针思想、常见动态规划模型、回溯入门最后一层是综合实战题大概15道覆盖文件操作、爬虫解析、数据处理、简单的可视化。你如果基础好可以直接跳到第三层但我不建议完全跳过基础层——它里面有些写法陷阱对老手也很有价值后面我会举例子。1.2 题目背后对应的真实能力模型这100道题表面上在考语法实际在训练三种能力读代码的能力、写代码的能力、调试代码的能力。很多初学者有一个误区觉得刷题就是“把答案背下来”。其实多刷几道就会发现题目之间是有共性的。比如“列表去重并保持顺序”和“字符串中找出第一个不重复字符”表面上是两道题底层都在考一个东西怎么用辅助数据结构记录元素的出现状态。前者用set配合遍历后者用字典计数或Counter。这种解题模式一旦建立起来不是记住一道题而是理解一类题。另外这套题的环境取向很务实。它不是OJ在线判题系统那种严格输入输出格式而是“函数实现型”为主。也就是说它要求你写一个函数接收参数、返回结果而不是写整个脚本。这个设计我很喜欢因为在实际工作中你的代码最终几乎都是以函数、类、模块的形式被别人调用而不是print一堆东西就完事。用函数来练题等于从第一天开始就在练“模块化思维”这个习惯后面写项目会非常受益。2. 刷题之前的环境准备与语法自查2.1 先把自己的Python环境搞干净刷题最怕的不是题难而是环境先出问题。套题里的代码大多是纯Python标准库就能跑通的少数综合实战题涉及requests、matplotlib等第三方库所以环境准备其实很简单但有几个细节值得花十分钟处理干净。我这里给一个直接从零开始的操作流程Windows、macOS、Linux都适用到Python官网下载对应你系统的安装包注意Windows安装时一定要勾选“Add Python to PATH”这是新手最容易漏的选项。不勾选的话后面在命令行里输python会提示“不是内部或外部命令”。安装完成后打开终端Windows叫PowerShell或CMD输入python --version看到类似“Python 3.10.12”的输出就说明安装成功。如果显示的是“Python was not found”大概率是PATH没配好重装一遍勾选选项即可。给项目单独建虚拟环境。不建议直接在全局环境里pip install一堆包后面包冲突会让人崩溃。在项目目录下执行python -m venv venv激活后一切依赖都装在虚拟环境内部删了文件夹就是完全卸载特别干净。配置pip国内镜像源。这一步不是必须的但如果你不配置用默认源下载matplotlib这种大包时速度能慢到劝退。在用户目录下新建pip.iniWindows或pip.confLinux/macOS写入清华源或阿里云源地址实测下载速度能从几十KB/s跑到几MB/s。编辑器方面VSCode和PyCharm二选一。新手我更推荐VSCode因为启动快、插件体系方便装一个Python扩展就能获得代码补全和调试能力。PyCharm功能更全面但对低配电脑不太友好。不管选哪个都要确认编译器指向刚才创建的虚拟环境路径否则会出现“终端能跑编辑器里却报模块找不到”的诡异问题。这一步里最容易踩的坑是明明pip list能看到包但运行脚本时还是提示ModuleNotFoundError。原因九成以上是解释器路径选错了——编辑器默认用的还是全局Python包装在虚拟环境里当然找不到。看到这个报错第一反应不是重装包而是去编辑器右下角或设置里切换解释器。2.2 性感的语法基础自查清单刷题之前有几个语法点值得先过一遍。不需要多深入但至少要达到“看到能用”的程度。这套题库里大量出现这些点提前扫盲可以省去刷题时频繁查资料的打断感。列表推导式这个几乎是必考写法上就是一个方括号加for循环再加可筛选条件。它不光是语法糖运算速度通常也比手写循环快一点因为内部的循环在CPython解释器层做了优化。题库里“生成符合条件的列表”“快速初始化二维数组”这类题列表推导式都是标准解。字典的get/setdefault/update很多人只会用dict[key]取值但刷题时经常会遇到“键不存在要给默认值”的场景。用get(key, default)或defaultdict这类工具能省掉三四行if判断。切片与步长列表和字符串的切片是Python独有的灵活语法s[::-1]可以实现字符串反转s[::2]可以取偶数位s[-1]取最后一个元素。这些在字符串处理类题目里用途极广。lambda、map、filter、sorted的key参数这四个组合在一起能写出非常简洁的代码。尤其是sorted的key参数在按字典值排序、按字符串长度排序、按对象属性排序时是标准解法。可变对象与不可变对象的区别列表、字典是可变对象字符串、元组、数字是不可变对象。这个区别在函数传参时特别重要——如果函数里修改了传入的列表外面的列表也会变很多人在这上面栽跟头。try/except题库的综合题部分会涉及文件读取、网络请求这些场景下异常处理不是可选项是必须有的。至少要知道捕获异常的基本写法以及什么时候该用except Exception而不是裸except。with语句管理文件资源、网络连接时使用with可以自动关闭资源防止句柄泄漏。刷文件操作题时建议强制自己用with open养成习惯对后面写工程代码有帮助。生成器表达式与大列表推导式的取舍当数据量大时生成器表达式比列表推导式更省内存因为它是惰性求值的。题库里有几道题专门让你统计超大数据量下的词频这个时候直接用列表推导式可能会把内存吃光。以上这些点不需要背定义刷到对应题目时自然就明白了。我的建议是先浏览一遍清单遇到不认识的语法点花几分钟看一个小例子刷题过程中再遇到你会有“原来如此”的顿悟感。这个记忆强度比单纯看教程高得多。3. 经典题目精讲思路拆解与参考答案这一章挑几道很有代表性的题目覆盖字符串处理、经典算法、动态规划、实战爬取四类每题我会说清楚思路、给出可运行代码再补充一些我自己的做题心得。3.1 字符串与列表用辅助结构降低解题难度第一道题写一个函数接收一个字符串返回其中出现次数最多的字符以及出现次数。如果有多个字符并列最多返回最先出现的那个。先把问题拆开它包含三个子需求一是统计每个字符的次数二是找出最大值三是在并列时按出现顺序优先。很多初学者看到第一反应是两层循环暴力统计第一层遍历所有字符第二层数每个字符出现几次时间复杂度是O(n²)字符串一长就卡。更优雅的做法是单次遍历用字典记录每个字符的出现次数同时维护一个“次数最多的字符”的临时变量。用字典统计是最符合直觉的方案代码更短def most_common_char(s): counter {} for ch in s: counter[ch] counter.get(ch, 0) 1 max_char max_count -1 for ch in s: # 这里第二次遍历字符串保证了并列时返回最先出现的 if counter[ch] max_count: max_char ch max_count counter[ch] return max_char, max_count我的心得是第一遍遍历负责统计第二遍遍历负责“按原始顺序破平局”。如果直接对字典items按值排序相同次数的字符会按字典序返回不符合“最先出现”这个条件。这个细节就是题目看点所在能在面试中区分出到底有没有想清楚。进阶一点如果用collections.Counter可以更简洁from collections import Counter def most_common_char(s): counter Counter(s) return max(counter, keycounter.get), max(counter.values())但注意这个写法在平局时返回的是字母序最小的不是最先出现的要对细节敏感。另一道高频题是“列表去重并保持原始顺序”。比如输入[1, 3, 2, 1, 5, 3]希望返回[1, 3, 2, 5]。思路很简单利用set的去重能力但不用set直接构造因为set是无序的而是用集合做“是否见过的标记”同时用列表保存结果顺序def dedup_keep_order(items): seen set() result [] for item in items: if item not in seen: seen.add(item) result.append(item) return result这个解法的时间复杂度是O(n)set的查找是O(1)空间换时间的经典用法。如果面试官要求不能额外用集合那就只能用两层循环但那是特殊情况日常开发不用自虐。这两道题放在一起看核心都是“用辅助数据结构降低复杂度”这个意识一旦形成后面很多题你都自动有思路了。3.2 递归与经典模型斐波那契数列与汉诺塔题库里递归题几乎必考。因为递归是后续树遍历、分治算法、动态规划的基础而且递归代码“看似简单写对很难”。先看最简单的斐波那契数列要求用递归实现def fib(n): if n 1: return n return fib(n - 1) fib(n - 2)这是入门写法但直接用它算fib(40)就会卡很久因为重复计算量太大。它递归展开是一棵二叉树每个节点都要重新算孙子节点时间复杂度是O(2^n)。很多刷题新手写完就以为自己搞定了但题目更想让你看到能用递归的也一定能用记忆化或迭代优化。记忆化版本from functools import lru_cache lru_cache(maxsizeNone) def fib(n): if n 1: return n return fib(n - 1) fib(n - 2)加上lru_cache这个装饰器一行代码函数会自动缓存结果时间立刻降到O(n)。这是Python刷题时的神兵利器遇到递归都建议先想想能不能记忆化。迭代版本def fib_iter(n): a, b 0, 1 for _ in range(n): a, b b, a b return a这个更省内存连递归调用栈都不需要了。刷题时建议把三种写法都自己敲一遍体会递归简洁和迭代高效的权衡。汉诺塔问题算是递归题里稍难的一个。题目有三根柱子A、B、CA柱上有n个大小不同的圆盘现在要把所有圆盘移到C柱每次只能移动一个圆盘且大盘不能压小盘。要求打印每一步移动过程。思路直接用递归三步法第一步将上面n-1个圆盘从A经C移到B第二步将最下面的一个从A移到C第三步将B上的n-1个圆盘经A移到C。这个思路如果光看书很容易觉得“玄学”但用代码表达出来非常短def hanoi(n, a, b, c): if n 1: print(f{a} - {c}) return hanoi(n - 1, a, c, b) print(f{a} - {c}) hanoi(n - 1, b, a, c)这段代码的核心不要纠结每一层具体做了什么而是相信“函数能正确移动n-1个盘子”这个假设直接用递归表达。这是递归思维的精华——问题分解和信任子问题。我第一次学的时候总想把每一步在脑子里模拟出来后来发现完全没必要你把“规模缩小的相同问题”递归交给下一层即可。这道题在后续二叉树遍历、快速排序中会反复出现同样的模式。3.3 动态规划入门最少硬币问题与01背包动态规划这块很多初学者觉得难是因为一上来就接触状态转移方程这种反直觉的概念。这套题库里选了“最少硬币”和“01背包”这两个经典模型我觉得是很好的切入点。它们共同的特点是看起来像是暴力搜索的问题但通过记录子问题结果能大幅降低时间复杂度。最少硬币问题给你一些面值的硬币比如[1, 5, 11]需要凑出金额n求最少需要多少个硬币。直接贪心做不一定正确——在[1, 5, 11]这套面值下如果你要凑15贪心先拿11剩下4需要4个1总共5个但最优解其实是3个5。所以必须用动态规划。定义dp[i]表示凑出金额i需要的最少硬币数初始化dp[0] 0其他为无穷大。状态转移公式是dp[i] min(dp[i], dp[i - coin] 1)。写成代码def min_coins(coins, amount): dp [float(inf)] * (amount 1) dp[0] 0 for i in range(1, amount 1): for coin in coins: if i coin: dp[i] min(dp[i], dp[i - coin] 1) return dp[amount] if dp[amount] ! float(inf) else -1这段代码我提个醒外层循环遍历金额内层循环遍历硬币面值理解起来漏一点就乱。你可以这样想要凑出金额i最后一步一定是放了某个coin那之前凑的金额就是i-coin所以dp[i]就等于dp[i-coin]1的某种最小值。这样推导方程就顺了。01背包问题在这里其实稍有难度但很经典。题目给定一个容量为capacity的背包和若干物品每个物品有重量weight和价值value每个物品只能选或不选求能装下的最大价值。二维dp版本思路dp[i][j]表示前i个物品背包容量为j时的最大价值。对每个物品要么不装dp[i][j] dp[i-1][j]要么装dp[i][j] dp[i-1][j-w] v。取两者较大值。def knapsack(weights, values, capacity): n len(weights) dp [[0] * (capacity 1) for _ in range(n 1)] for i in range(1, n 1): w weights[i - 1] v values[i - 1] for j in range(1, capacity 1): if j w: dp[i][j] max(dp[i - 1][j], dp[i - 1][j - w] v) else: dp[i][j] dp[i - 1][j] return dp[n][capacity]刷完二维版本可以再挑战一维滚动数组优化。这个优化会让初学者摸不着头脑但你只需要记住一个关键点内层循环要倒着遍历容量。这是因为正着遍历会让同一个物品被重复装入多次就违背了“01背包”的原则。理解这个点之后一维优化也不过是几十秒的事情。3.4 综合实战爬虫解析、数据清洗与可视化题库最后一部分的题目贴近真实场景。让我挑一个组合题来讲解用requests从某个公开API或网页抓取数据解析出自己需要的字段做一次简单的数据清洗最后用matplotlib画一张分布图。这种题把爬虫、字符串处理、数据结构、可视化串在了一起是检验综合能力的好方式。先模拟一个场景抓取一个页面上所有的图片链接筛选出其中包含“logo”关键词的图片统计不同域名下的数量画出饼图。这个过程分三步第一步是数据获取。这里我给一个安全且稳定的示例——用requests获取一个公开API接口的数据而不是随便找一个具体网站抓取。因为网页结构经常变示例写得太死你看完第二天就可能失效。核心代码结构import requests resp requests.get(https://api.example.com/data, timeout10) if resp.status_code 200: data resp.json() else: print(f请求失败{resp.status_code})第二步是数据清洗和统计。这里会用列表推导式、字典、Counter。假设data是一个列表每个元素是一个字典包含url和type字段from collections import Counter logos [item[url] for item in data if logo in item.get(type, ).lower()] domain_counter Counter() for url in logos: # 简单提取域名实际要用urllib.parse更严谨 domain url.split(/)[2] domain_counter[domain] 1第三步是可视化用matplotlib画饼图import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False labels list(domain_counter.keys()) sizes list(domain_counter.values()) plt.pie(sizes, labelslabels, autopct%1.1f%%) plt.title(Logo图片的域名分布) plt.show()有两处细节我特别想提醒你。第一matplotlib默认字体不支持中文如果不设置字体为SimHei或其他中文字体图中的中文全是方块。第二requests请求一定要设置timeout参数否则目标站点无响应时你的脚本会一直挂在那里这是真实爬虫和教程爬虫最大的差别。题库里的综合题虽然简化了场景但这些工程化的习惯从第一天起就要建立起来。4. 刷题过程中的高频报错与排查技巧刷题过程中遇到报错是常态没有一个程序员是“一遍过”写完所有题的。这一章把常见到分不清是环境问题、语法问题还是逻辑问题的报错集中整理一下。4.1 环境依赖类问题速查报错表现大概率原因解决方案ModuleNotFoundError: No module named requests包没安装或安装到了别的环境先pip install requests然后在代码中print(sys.executable)确认解释器路径ImportError: libpython3.x.so.1.0: cannot open shared object fileLinux下Python编译时没有开动态库通常出现在用源码编译Python的环境建议改用apt安装或pyenv管理运行VSCode后import正常但PyCharm里表示找不到包PyCharm默认创建的虚拟环境与终端不一致在PyCharm的Settings中把Project Interpreter切换到正确的虚拟环境路径pip install时提示“Consider using the --user option”当前环境是系统级Python权限受限不用换命令最好先创建虚拟环境再安装环境问题最让人恼火因为它跟你的代码逻辑完全无关却能让你怀疑人生。我自己的排查顺序永远是先确认解释器路径再确认包列表最后才看代码。不要一上来就重装Python那样往往把简单问题复杂化。4.2 语法与逻辑错误排查实录语法错误相对好排查因为报错信息会直接告诉你在第几行比如IndentationError、SyntaxError。真正难的是逻辑错误——程序能跑结果不对。这里我分享几个自己的高频自查习惯。第一个是“打印中间值法”在关键计算步骤前后加print看变量是否符合预期。比如动态规划题你会打印dp数组的中间变化很容易定位是初始化错了还是转移方程写错了。第二个是“边界值测试”每次都拿最大输入、最小输入、空输入分别测一下。很多逻辑错误只会在边界条件下暴露比如列表为空、金额为0、物品数量为1。第三个是“代码走读法”把代码像讲故事一样用自然语言说一遍很多时候说着说着就能发现逻辑漏洞这比死盯着屏幕更有效。常见的逻辑错误还有这几类索引越界循环在边界判断时少写了或访问了list[len(list)]。建议访问列表最后一个元素时用list[-1]可读性和安全性都更好。深浅拷贝混淆在二维列表初始化上最容易踩坑[[0] * 5] * 3创建的三行其实是同一个list对象修改一行其他两行全变。正确写法是[[0] * 5 for _ in range(3)]。可变默认参数函数定义def f(lst[])时默认列表只会创建一次多次调用会累积数据。正确做法是用None占位函数内再创建新列表。for循环中修改被遍历的序列遍历一个列表时如果想删除其中某些元素直接remove会导致跳过元素或索引错乱。正确做法是遍历副本或者用列表推导式生成新列表。4.3 用调试器而不是print硬扛很多人刷题调试只用print这在小规模题目里完全够用但一旦逻辑复杂print的劣势就显现出来——你得自己脑补执行顺序还要频繁删除调试代码。我的建议是花半小时学一下Python内置的pdb或VSCode的调试面板。VSCode里用起来很顺手在代码行号左侧点击红点设置断点按F5启动调试程序就会停在断点处。这时可以看左侧的“变量”面板查看每一层的变量值也可以按F10逐行执行F11进入函数内部ShiftF5退出调试。这样你能直观地看到每一步变量的变化逻辑错误基本一眼就能定位。pdb适合在命令行里快速调试在代码中插入import pdb; pdb.set_trace()运行到这一行就会进入交互式调试界面可以输入变量名查看值输入n继续下一行。我个人觉得VSCode的可视化调试更适合初学者因为它不需要记命令所见即所得。花一次30分钟的学习时间后面100道题都能用上这笔投资非常划算。5. 正确刷题姿势从“做过”到“会做”的三刷法拿到100道题千万不要按顺序从头刷到尾。我用三轮刷法效果远好过一遍到底。第一轮是“扫荡”目标是过语法、找手感。这一轮不要纠结某道题卡了多久卡住10分钟没思路就跳过看答案也完全没关系。把100道题快速扫完标记出“秒做”“思考后做出”“看答案才懂”三类题目。这一轮大概用时两周左右。第二轮是“攻坚”目标是只看自己标记的“思考后做出”和“看答案才懂”的题目重新独立做一遍。如果还是不会就把这类题目单独抽出来做一个“错题本”记录题目要求和核心思路而不是粘贴大段代码。错题本建议用手写或纯文本维护关键是把自己当时的卡壳点写清楚比如“没想到用字典记录下标”“忽略了边界条件”。第二轮大概用一周。第三轮是“提速”目标是在限定时间内完成题目。给自己设定每题15分钟或20分钟的限制模拟面试或实际工作的压迫感。这一轮还可以尝试一件事限制自己只能使用标准库、不能使用第三方库这能锻炼你手写实现能力。真正到了面试或写核心逻辑时不依赖现成库手写算法还是很有必要的。三刷法背后的逻辑就是检索练习——第一次接触是建立印象第二次是主动回忆第三次是自动化。只刷一遍你大概率只能记住“我做过这题”而不是“我能独立写出这题”。再补充一个“让刷题更接近实战”的升级玩法把每道题都改造成带单元测试的函数。比如你写完去重函数后再写一个test_dedup函数用几个断言来验证正确性def test(): assert dedup_keep_order([1, 3, 2, 1, 5, 3]) [1, 3, 2, 5] assert dedup_keep_order([]) [] assert dedup_keep_order([1, 1, 1]) [1] print(所有测试通过)这个习惯的真实价值在于你的代码不再靠“肉眼验证”正确性而是交给机器去验证。一旦后面你也开始写项目、做重构测试会变成保护你代码安全的网。100道题练下来相当于练了100个最小可测试单元这笔隐形收获很大。刷完这100道以后下一步怎么做我的建议是不要直接去刷LeetCode先做一个小项目把题库里练到的列表推导、字典统计、文件操作、requests请求串起来。比如做一个“命令行词频分析工具”输入一个文本文件路径输出出现频率最高的10个单词以及词频分布柱状图。这个项目会用到大概10道题的知识点是一个性价比非常高的收尾练习。就我个人经验来看很多人刷题失败不是毅力不够而是节奏不对。一天想刷20道然后被某道动态规划卡住心态崩了就再也不刷了。更好的办法是固定每天3道雷打不动遇到不会的先跳过第二天再回来。刷题是一个长跑不是冲刺。“每天都能碰代码”比“某天拼命写很多”要重要得多。最后一句话送给想真正把Python学扎实的朋友题库只是地图你才是走路的人。100道题只有真正上手敲一遍、报错一遍、调试一遍、回头复习一遍才算是自己的东西。希望这套练手题能成为你Python路上的第一块扎实的台阶。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进