☰
Python题库doc文档清洗与自动化练习全指南
2026/10/18 0:13:50 网站建设 项目流程

简介:这是一份Python编程题库文档,面向正在学习Python基础语法、准备期中期末或等级考试的学生,也可用于教师出题参考。内容由选择题和程序填空两大板块组成,选择题覆盖逻辑运算与布尔值、列表max取最大值、变量内存管理、input/print区分、浮点除法、区间表达式、相等关系写法等高频考点;程序填空部分则涉及蒙特卡洛方法求圆周率、奇偶校验码判断、滴滴快车分段计费规则等实际场景应用题,并附有部分答案与解析。资源为单独一个doc文件,体积约220KB,便于直接打开浏览、筛选或打印练习。文档整合了Python核心知识点的典型题目与配套解析,读者可对照答案解析逐步理解常见易错点和算法实现思路,适合用于考前突击、自测巩固和日常课堂训练。已有269人学习使用,对希望系统梳理Python基础的初学者有较强的参考价值。

1. 拿到“Python题库.doc”之后,先别急着做题

很多初学者或培训讲师电脑里都躺着这样一个文件:别人传的、网盘下的,或者自己攒了好几年的“编程(完整版)Python题库.doc”。文件不小,目录看着也挺全,从基础语法到文件操作、异常处理、面向对象甚至网络编程都有覆盖。但真打开准备练的时候,问题立刻就来了——格式乱、代码缩进全没了、中文引号变成乱码,复制一段题目的代码进解释器直接报错。我见过太多人卡在这一步就放弃了,转头去刷在线网站,回头又说文档题库没用。

这篇文章不打算夸这份题库有多好,也不替它背书。我想说的是:一个 .doc 格式的 Python 题库,到底怎么处理才能变成真正能用来刷题、备课、甚至二次出题的东西。核心就三件事——把文档转成干净可用的格式,把题目按质量筛一遍,再让它能高效地进到你的练习闭环里。这套流程我自己用在不同来源的题库上反复做过,每一步都有坑,也有对应的解决办法。新手能照着操作,熟手可以直接跳到格式转换和自动化抽题的部分看参数。

2. 先把 .doc 转成 .docx 或 .md:为什么这一步不能省

2.1 .doc 老格式的三大坑:乱码、缩进丢失、不可批量处理

.doc 是微软老一代的二进制格式,和 .docx 的 OpenXML 结构完全不同。它最大的问题是“文本和格式揉在一起”,解析难度大,很多现代的文本处理工具对它支持都不好。具体到 Python 题库这种场景,三个坑最明显。

第一个坑是编码。老 .doc 文件默认用 ANSI 编码保存,里面如果带了中文全角引号、长破折号这类字符,用 Python 的 open() 直接读经常报 UnicodeDecodeError,或者读出来是一堆 “锟斤拷”。第二个坑是代码缩进。Python 代码靠缩进区分块级结构,但 .doc 里的缩进很多是用制表符、全角空格或者 Word 的自动编号混合实现的,复制到 IDE 里要么全部顶格,要么混进奇怪字符。第三个坑是批量操作困难。你想把题库按章节拆开、或者提取所有代码块,用 .doc 就得依赖 win32com 调起 Word 进程,慢且容易卡死。

所以拿到文件第一步,先把它转成 .docx。这不是玄学,而是 .docx 本质是一个 zip 包,里面是 XML 文本,Python 有成熟的 python-docx 库可以直接解析,数据是结构化的,代码块和普通段落可以区分处理。

2.2 用 LibreOffice 批量转格式:最稳的命令行方案

我一般不用 Word 自带的“另存为”,因为没法批量操作几十个文件。常见做法是装一个 LibreOffice,然后用它的 headless 模式在命令行批量转换。LibreOffice 对 .doc 的兼容性做得比较成熟,转出来的 .docx 基本不乱码。

# 批量把当前目录下所有 .doc 转成 .docx soffice --headless --convert-to docx --outdir ./converted ./source/*.doc

这段命令的逻辑很直白:--headless表示不启动界面,--convert-to docx指定输出格式,--outdir指定输出目录。./source/*.doc是通配符,一次处理文件夹里所有 .doc 文件。如果你只想转一个文件,就把最后一个参数改成具体文件名。

参数说明:注意soffice命令在 Windows 上一般叫soffice.exe,如果提示找不到命令,检查 LibreOffice 的安装路径有没有加进系统环境变量。另外,LibreOffice 转出来的 .docx 可能会把原来用自动编号的列标题转成纯文本数字,这个不影响后续使用。转换完成后打开几个文件抽查一下,重点看代码段是不是还保持缩进。

如果文件实在太大,转 .docx 之后打开还是卡,还有一个更激进的办法——直接转成纯文本 .md 格式。这样做会丢失所有加粗、标题样式,但好处是所有内容变成纯文本,缩进问题反而好处理了。

2.3 用 python-docx 抽代码块:把答案和题目切开

转成 .docx 之后,就可以用 python-docx 做更精细的操作。一个很常见的需求是:把题目和答案拆开。很多题库文档的结构是“题目描述下面紧跟答案代码”,或者“题目标题用加粗,答案代码用等宽字体”。我们要做的就是识别这些特征,然后分开保存。

# 读取 docx,按样式拆分题目和答案 from docx import Document import re doc = Document('converted/编程(完整版)Python题库.docx') questions = [] answers = [] for para in doc.paragraphs: text = para.text.strip() if not text: continue # 判断题型:题目一般以数字加顿号或点开头 if re.match(r'^\d+[.、.]', text): questions.append(text) continue # 判断是否为代码块:以空格或 tab 开头,或者包含常见关键字 if text.startswith((' ', '\t')) or ('def ' in text or 'print(' in text or 'import ' in text): answers.append(text) continue # 其他正文段落归入题目区 questions.append(text) print(f"提取到 {len(questions)} 段题目描述,{len(answers)} 段疑似代码块")

逻辑说明:这段代码先把段落按空白过滤,然后用正则判断是否以数字开头,是就认为是题目标题。代码块的判断用的是“以空格开头或包含关键字”的规则,实际效果对大部分题库能达到 70%~80% 的准确率。为什么不是100%?因为有些题库的代码块并没有统一的前缀,有的用三个空格缩进,有的用制表符,有的代码里根本没有def和print。

更稳的方案是:打开 .docx 看段落对象里的style.name。如果题库是用 Word 排版过的,题目可能是List Paragraph样式,代码是Normal且带左缩进。这时判断条件改成para.paragraph_format.left_indent是否为特定值,识别率会高很多。你在自己的文档上可以先跑一版统计,看看缩进用的到底是空格还是 tab,再决定判断规则。

3. 让题库“能练起来”:清洗题目和代码的五个标准动作

3.1 统一代码缩进:把全角空格和制表符全部换掉

从 Word 里复制出来的代码,最致命的就是缩进字符混杂。Python 解释器对缩进极其敏感,一个全角空格混进去就会报IndentationError: unindent does not match any outer indentation level。清洗这一步的目标只有一个:让每一段代码变成能用解释器直接跑的状态。

# 清洗单个代码块 def clean_code_block(code_lines): cleaned = [] for line in code_lines: # 把全角空格替换成普通空格 line = line.replace('\u3000', ' ') # 把制表符替换成四个空格 line = line.replace('\t', ' ') # 去掉行尾多余空白 line = line.rstrip() cleaned.append(line) return '\n'.join(cleaned) raw_code = """def hello(): \tprint("你好") \u3000\u3000return 1""" print(clean_code_block(raw_code.split('\n')))

逻辑说明:\u3000是全角空格的 Unicode 编码,这个字符在中文输入法下极易混入代码。替换规则是统一成普通半角空格,再把制表符展开成四个空格。为什么要展开成四个空格而不是一个 tab?因为绝大多数 Python 编辑器默认把 tab 显示成 4 个空格,但如果代码里混用 tab 和空格,解释器会直接报错。统一成空格能彻底避开这个问题。

参数说明:如果原始题库用的是 2 空格缩进风格,把' '改成' '就行。但 Python 官方 PEP 8 推荐 4 空格,建议清洗时统一按 4 空格来。另外注意字符串里的\t别被误替换——如果你在代码里写了print("a\tb"),这个\t在字符串常量里,不应该动它。但上面的逻辑没有区分字符串内和行首的 tab,这是一个简化处理。严谨一点的做法是用 tokenize 模块去判断,不过对题库练习场景来说,误伤的概率很低,手工修一下也快。

3.2 处理中文引号与编码问题:一道经典的“乱码”题目

中文题库里最常见的问题是:代码里的字符串用了中文引号,比如print(“hello”)。这在 Word 里看起来很正常,但 Python 解释器只认 ASCII 引号,直接运行就是SyntaxError: invalid character '“' (U+201C)。

# 把全角引号替换成半角引号 import re def fix_chinese_quotes(code_text): # 只处理成对出现的引号,避免误伤字符串内容 # 把左双引号替换为 ASCII 双引号 code_text = code_text.replace('“', '"') # 把右双引号替换为 ASCII 双引号 code_text = code_text.replace('”', '"') # 单引号同理 code_text = code_text.replace('‘', "'") code_text = code_text.replace('’', "'") return code_text sample = 'print(“hello world”)' print(fix_chinese_quotes(sample))

逻辑说明:这个规则是暴力替换,风险在于如果字符串内容里故意用了全角引号来表达某种文本效果,会被一起改掉。但对题库代码来说,全角引号基本都是键盘误输入,直接替换是收益最高的做法。真正注意的地方是注释里的内容不需要改,改了也不影响运行,所以暴力替换的副作用可以接受。

更进一步,如果整个文件读进来出现UnicodeDecodeError,多半是编码没对上。常见做法是强制用 GBK 或 UTF-8 读取,失败就换另一种:

# 自动检测编码并读取文件 def read_text_smart(filepath): for enc in ['utf-8', 'gbk', 'gb2312', 'latin-1']: try: with open(filepath, 'r', encoding=enc) as f: return f.read() except UnicodeDecodeError: continue raise RuntimeError(f'文件 {filepath} 无法用常见编码解码')

这个函数的逻辑是逐个尝试编码方案,latin-1是最后兜底,它不会报错但读出来是乱码。用这个函数能快速定位文件真实的保存编码。我自己处理过的题库里,GBK 编码的 .txt 文件占了相当比例,尤其是直接从 Windows 老机器上拷出来的。

3.3 代码块切分:解决“题干里混着代码”的边界问题

整个题库清洗中最费时间的是切分。很多题目的结构是:

3. 编写一个函数,计算列表的平均值 def average(lst): return sum(lst) / len(lst)

但如果题干里也出现了“例如:print(1)”,就会被误判成代码块。所以要按“连续代码行”来切分,而不是按单行判断。

# 按连续行切分代码块 def split_code_blocks(lines): blocks = [] current = [] for line in lines: # 空行可能出现在代码中间,也可能是段落分界 if line.strip() == '': if current: blocks.append(current) current = [] else: # 行首有缩进,或者是 Python 语法关键字开头 if line.startswith((' ', '\t')) or line.startswith(('def ', 'class ', 'import ', 'from ', 'print(', 'if ', 'for ', 'while ')): current.append(line) else: if current: blocks.append(current) current = [] if current: blocks.append(current) return blocks

逻辑说明:split_code_blocks把文本按行读入,遇到空行或连续的非代码行就认为一个代码块结束。好处是可以在块级别做后续处理。比如你筛选“某一段代码能否单独运行”,只需要把blocks里的每一项写进临时文件再执行。

这里有个常见误区:不要试图用正则一行匹配所有代码行,因为题库排版总是会给你惊喜。比如有的题把代码用表格框起来、有的代码带有行号前缀1.、有的代码和题干之间没有空行。遇到这种,手工补一个空行比写自动化规则更省时间。清洗工具处理掉 80% 的活,剩下 20% 用手工,是效率最高的配比。

3.4 用 py_compile 做语法校验:一分钟找出所有还不能跑的代码

清洗之后的代码块是不是真的能跑,用py_compile可以快速做整体校验。这个模块不执行代码,只编译检查语法,速度很快。

# 批量语法校验 import py_compile import tempfile import os def validate_code_blocks(blocks): valid_count = 0 syntax_errors = [] for idx, block in enumerate(blocks): # 每个代码块写入临时文件 with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False, encoding='utf-8') as f: f.write(block) temp_path = f.name try: py_compile.compile(temp_path, doraise=True) valid_count += 1 except py_compile.PyCompileError as e: syntax_errors.append((idx, str(e))) finally: os.unlink(temp_path) print(f"校验 {len(blocks)} 个代码块,通过 {valid_count} 个,失败 {len(syntax_errors)} 个") return syntax_errors

逻辑说明:每个代码块独立写盘再编译,目的是模拟“单独运行”的场景。因为题库里有些代码是片段,需要依赖前面的上下文变量才能跑,这类题py_compile是查不出来的,它只能查语法。校验结果里失败的代码块,基本就是题目本身写错了、排版乱了,或者是答案里带了省略号。这些就是需要人工处理的少数派。

参数说明:doraise=True表示有错就抛异常而不是打警告,这样方便统计。suffix='.py'保证文件后缀是 .py,否则 py_compile 会拒绝编译。这里还会踩一个坑:Windows 的临时目录可能有权限限制导致NamedTemporaryFile删除失败,如果报 PermissionError,把os.unlink改成os.remove并加 try 处理就行。

4. 判断这份题库值不值得深挖:三类题目分级法

4.1 值得逐题练的题目特征

清洗完之后,第一件事不是急着刷题,而是先对题库做“质量分级”。我处理过几份不同的 Python 题库文档,发现里面的题目质量差异极大。有些题是典型的“为了出题而出题”,比如无意义地让你计算一个很大的阶乘;有些题则非常贴合面试和实战,比如让你封装一个带错误处理的文件读取函数。

值得优先做的题目通常有这几个特征:

  • 题目描述里给出了明确的输入输出示例,比如“输入 [1, 2, 3],输出 6”,这种题目的结果是可验证的,做完能立刻知道对错。这是知根知底的题。
  • 题目涉及的知识点组合不单一。比如同时考函数定义、循环和列表操作,比单纯考for i in range(10)更有价值。
  • 有边界条件的暗示,比如“假设列表可能为空”“输入可能是负数”。这类题会让你养成考虑边界情况的习惯,往小了说是刷题,往大了说是工程习惯。

反过来,如果题目描述只有一句话“请编写一个函数”,没有任何输入输出说明,这种题做起来非常难受——你都不知道答案长什么样才算对。这类题我建议先跳过,等整本题库做完一遍再回头补。

4.2 用难度和知识点打标签:给题库做目录和索引

题库文档里一般自带目录,但那只是章节分类,比如“第3章 循环语句”。真要用来复习和练习,最好自己做一套标签。我给题库打标签时常用的维度有两个:难度(入门 / 进阶 / 挑战)和知识点(列表 / 字典 / 函数 / 递归 / 文件 / 装饰器)。实际操作时,不需要写代码,用 Excel 就足够。

整理出来的索引表结构推荐这样:

题号章节难度知识点代码状态备注
1基础语法入门变量、print通过无需修改
17循环进阶while、break通过边界条件需注意
34函数挑战递归、列表推导语法错误题目本身有问题

这个表的价值在于复习阶段。你不需要从头到尾再刷一遍,直接筛选“知识点=递归”或“难度=挑战”,就能精准定位。

打标签阶段最重要的原则:不要贪多。标签控制在 2~3 个维度就够了。我之前见过有人给每道题打了七八个标签,最后维护成本比刷题成本还高,这种过度设计反而是翻车的前兆。题库的价值在练不在存,做完标签赶紧进入下一个环节。

4.3 不值得做的题:直接标记删除或合并

有个我一开始很舍不得,后来发现必须做的事:删题。有些题明显是过时或错误的,比如让你用xrange(Python 2 专用),或者让你用已经移除的os.system来做某种操作。这些题放进练习列表只会浪费半小时,然后让人困惑“为什么我的代码报错”。

常见的处理策略是:

  • 标记为“已废弃”,不再安排练习。
  • 把同一知识点的重复题合并掉。比如连续 10 道“冒泡排序变体”,留 2~3 道做透就够了。
  • 对答案有争议的题,比如“下面哪个写法更好”这类主观题,如果你只是一个人刷题,直接删掉,因为没人讨论价值很低。

在题库文档上做这些操作,我没用自动化脚本,而是用 Word 的批注功能直接标记。因为判断一道题要不要留,本质是主观判断,脚本帮不上忙。这个过程像在整理自己的工具箱——留下的每样东西都要用得上,用不上的就果断扔,留着只会增加每次翻找的成本。

5. 自动化抽题与批量练习:把题库变成每日训练场

5.1 写一个随机抽题脚本:按难度和知识点出题

题库整理好了,接下来让它发挥作用。最常见的使用方式是随机抽题。这里有个参数很关键:怎么定义“随机”。如果完全随机,可能会出现连续抽到同一个知识点的题;如果按权重抽,就能控制训练覆盖面。

# 按知识点权重随机抽题 import random questions = [ {"id": 1, "topic": "list", "difficulty": "easy", "content": "..."}, {"id": 2, "topic": "dict", "difficulty": "medium", "content": "..."}, {"id": 3, "topic": "list", "difficulty": "hard", "content": "..."}, ] def pick_questions(q_list, count=5, topic_weights=None): if topic_weights is None: topic_weights = {"list": 2, "dict": 1} # 按权重展开候选池 pool = [] for q in q_list: weight = topic_weights.get(q["topic"], 1) pool.extend([q] * weight) return random.sample(pool, k=min(count, len(pool))) # 抽 3 道列表题,1 道字典题(通过权重体现) selected = pick_questions(questions, count=3, topic_weights={"list": 2, "dict": 1})

逻辑说明:pool.extend([q] * weight)是“按权重放大候选池”的经典做法。列表权重为 2,那么列表题被抽中的概率就是权重 1 的字典题的两倍。random.sample做不放回抽样,避免同一道题被重复抽到。

这个脚本的参数不需要太多,三个就够用——抽题数量count、知识点权重topic_weights、以及从哪些题里抽q_list。权重值不需要精确,它是相对关系。如果连续抽出来的题太难或太简单,调一下权重比例就行。

换一个更精细的思路,你现在真正需要的是能按“遗忘曲线”排题的脚本,也就是根据你上次做错的情况,把错题加权抽出来。这个怎么做在后一章讲。先跑通抽题脚本的意义在于:你能在没有心理负担的时间里,每天固定看到几道不一样的题,形成刷题节奏。

5.2 让代码自己跑:用 subprocess 执行答案并抓取输出

练习编程题有一个痛点是:写完代码不知道对不对。如果题库里带答案,比较好的使用方式是——你先自己写一版,再用 subprocess 去跑这一版和答案版,对比输出。这正是让题库“完整版”的价值发挥出来的地方。

# 运行代码块并捕获输出 import subprocess import sys def run_code_snippet(code, timeout=5): with open('temp_code.py', 'w', encoding='utf-8') as f: f.write(code) try: result = subprocess.run( [sys.executable, 'temp_code.py'], capture_output=True, text=True, encoding='utf-8', timeout=timeout ) return { 'stdout': result.stdout, 'stderr': result.stderr, 'return_code': result.returncode, } except subprocess.TimeoutExpired: return {'stdout': '', 'stderr': '运行超时', 'return_code': -1} code = """ for i in range(3): print(i) """ print(run_code_snippet(code))

这里先写临时文件再执行的原因在于:subprocess 需要接收一个可执行文件路径,不能直接传字符串代码。sys.executable获取当前解释器路径,保证用的是同一套 Python 环境。capture_output=True捕获标准输出和错误流,text=True让输出以字符串形式返回而不是字节。

参数说明:timeout=5最好根据题目复杂度设置。递归或全排列类题目可能跑得慢,可以调到 10;而基础题 2 秒就够。超时返回-1用于标识“这道题可能有死循环”。这个功能最大的价值在于批量验证答案。你把题库里所有代码块跑一遍,就能确认哪些答案是能跑的,哪些自带 bug。

不过需要注意的是:题库里有少量题目会调用input()等待键盘输入,如果直接跑会卡住直到超时。解决方法是先检测代码里有没有input(,有就跳过或者传入预设输入。关于这个“input 陷阱”我在后面避坑还会再提。

5.3 用 pytest 包装练习题:把题目转成“可测试用例”

练习到一定量之后,你可能会想做一件事:把某些经典题变成自动化测试用例,以后每次改动代码都能回归验证。用 pytest 就能把题目包装成用例。

# 把题库第 7 题包装成 pytest 用例 # 题目:实现 add(a, b) 返回两数之和 def add(a, b): return a + b def test_add(): assert add(1, 2) == 3 assert add(0, 0) == 0 assert add(-1, 5) == 4

逻辑说明:test_add是 pytest 识别测试函数的固定前缀。写三个断言覆盖正数、零、负数场景,执行pytest就能看到测试是否通过。这个思路从“刷题”变成了“测试驱动”,工程味道更浓。

参数说明:断言数量不用多,三道五个就能覆盖主要分支。重点测试边界,比如空列表、单个元素、极大值。如果题目答案里自带测试用例,优先复用;如果没有,自己补边界的成本不高。

这节的目的是让你体会到:整理题库最终不是为了“看完”,而是为了“跑起来”。哪怕是同一道题,用 pytest 包装之后,它就从静态文字变成了一个可以重复执行、可以验证的分支逻辑。新手用这个方式能建立“代码是对是错”的判断标准,熟手能把它接进自己的练习框架。

6. 避坑指南:处理 Python 题库的五个高频翻车现场

6.1 现象:UnicodeDecodeError: 'gbk' codec can't decode byte 0x80

原因:文件实际是 UTF-8 编码,但你用 GBK 打开。这种情况常见于从网上下载的 .doc 被转成 .txt,或者文档里有特殊表情符号。

解决:按前面read_text_smart的方式做编码探测,或者直接用编辑器(如 VS Code)打开后看右下角编码提示。稳妥做法是统一转成 UTF-8 无 BOM 格式再处理:

# 强制写入 UTF-8 无 BOM with open('output.py', 'w', encoding='utf-8') as f: f.write(cleaned_code)

6.2 现象:代码能编译但运行报NameError: name 'xxx' is not defined

原因:题目答案是一个片段,依赖前文定义的函数或变量。题库文档为了篇幅会把上下文省略掉,直接复制片段运行必然报错。

解决:遇到这种题,不要硬跑。把缺失的变量定义补一个简单的默认值放在代码块开头。比如题目说“利用已有的 data 列表”,你就在代码块前加一行data = []。如果题目本身没有说明 data 是什么,这一道题可以直接跳过,多半题目质量不行。

6.3 现象:代码运行后卡住不动,直到超时

原因:题目包含input()等待用户输入,或者代码里有死循环。题库里有些题在while循环里漏了条件更新,跑起来就是死循环。

解决:优先检测input(,有的话给代码追加预设输入:

# 自动追加 input 模拟输入 code = code.replace('input()', 'input("模拟输入")')

更稳的办法是运行前设置一个比较短的超时,比如 3 秒。超时就把这道题标记为“需人工检查”,不要让它一直挂住整个练习流程。

6.4 现象:全角空格导致若干行代码看起来对齐,但一跑就报IndentationError

原因:中文输入法在代码缩进的位置误输空格,尤其是复制粘贴的时候,Word 自动把半角空格替换成全角空格。这行代码在文本层面多了一个不可见字符,编辑器可能没显示出来。

解决:用正则把行首和行尾的全角空格全部清掉:

# 清洗行首行尾的全角空格 lines = [re.sub(r'^[\u3000]+|[\u3000]+$', '', line) for line in lines]

只消行首行尾是因为行中间的全角空格通常在字符串常量里,清洗会影响输出结果。行首行尾属于格式问题,必须清。

6.5 现象:题库里的答案是 Python 2 语法,比如print "hello"

原因:文档太老,里面是 Python 2 时代的代码。print作为语句而非函数,在 Python 3 下直接SyntaxError。

解决:对这类题有两个选择。一是用 2to3 工具批量转换语法:

# 使用 2to3 将 Python 2 代码转为 Python 3 2to3 -w legacy_code.py

二是直接把题目标记为“已过时”,跳过不练。我建议后者优先——因为老题库里的 Python 2 题不仅语法过时,思维方式也可能过时(比如不强调异常处理、不强调上下文管理器),练了反而会形成不好的习惯。

7. 进阶玩法:把题库接进每天的自动化练习流程里

整个题库处理到最后,最让人舒服的状态是:每天打开终端跑一个命令,今天该练的题就出现在屏幕上,做完自动记录对错,第二天按遗忘曲线安排复习。我给自己搭的这套流程,核心是一个practice.py脚本,配合一个简单的 JSON 文件存错题记录。

脚本核心逻辑不复杂:从题库里按权重抽题,把题目打印到终端,等你写完答案后手动输入y/n表示是否通过。通过的就标记“今天已完成”,没通过的进入错题表,下次抽题时权重提高 3 倍。

# 每日练习调度脚本(简化版) import json import random from datetime import date def load_progress(): try: with open('progress.json', encoding='utf-8') as f: return json.load(f) except FileNotFoundError: return {"wrong_questions": []} def save_progress(data): with open('progress.json', 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) def pick_daily(questions, wrong_ids, count=3): # 错题权重放大 3 倍 pool = [] for q in questions: weight = 3 if q['id'] in wrong_ids else 1 pool.extend([q] * weight) return random.sample(pool, k=min(count, len(pool))) progress = load_progress() daily = pick_daily(questions, progress['wrong_questions'])

这里值得注意的地方是ensure_ascii=False。如果不加这个参数,JSON 里的中文题面全会变成\uXXXX转义序列,读起来完全没法看;加上之后直接存原始中文,可读性提升巨大。indent=2则是让 JSON 文件按两空格缩进,方便你打开看结构,也方便 git 做差异对比。

错题记录用 JSON 而不是数据库,原因是练习场景数据量很小、结构简单,JSON 文件用文本编辑器就能查看和修改。你在做错一道题之后,把它加入错题列表,不需要额外的“复习状态机”,只要权重放大,它自然会在未来几天的抽题里高频出现,直到你连续答对几次后因为不再进入错题列表而自然“毕业”。这个手法的好处是:零额外依赖,又能把“反复做错题”这个目标不动声色地嵌在流程里。

最后想给一个经验之谈:一份题库文档的价值,往往不在题量多少,也不在答案是否完美,而在于你能不能让它持续产生反馈。我见过有人整理完题库后雄心勃勃定了一堆计划,最后因为流程太重、每天要打开三个工具才能开始刷题而放弃了。我的习惯是:所有步骤能合并就合并,最终只留一个命令、一个文件。如果你整理完题库,能有一个类似“打开终端,输入 python practice.py,进入今日题目”的一键闭环,这个题库就算是真正兑现了价值。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询