用Python从零实现科目一模拟答题系统

发布时间:2026/8/30 18:32:39
用Python从零实现科目一模拟答题系统 “科目一离开的太快就像龙卷风我会想你的”这句略带调侃的感叹最近经常出现在刚考完驾照科目一的朋友圈里。紧张备考的日子像龙卷风一样呼啸而过考试一结束很多人发现题目还没刷透知识点已经开始在脑海里“蒸发”了。这篇文章不打算罗列科目一知识点而是换个思路——用 Python 从零实现一套“科目一模拟答题系统”。通过一个可运行的命令行项目把 Python 语法、SQLite 数据库操作、JSON 数据处理、命令行交互、异常处理等知识点串起来。项目本身并不复杂适合刚学完 Python 基础、想找第一个完整项目练手的读者如果你正准备科目一考试也可以把官方题库整理成 JSON 后导入系统用随机组卷和错题回顾来提高刷题效率。1. 背景科目一复习为什么容易“看完就忘”科目一考试的全称是“道路交通安全法律、法规和相关知识考试”主要考察交通信号、道路通行规则、酒驾处罚标准、事故处理流程等内容。它的特点是题库大、知识点碎、细节多。一个同样涉及“超车”的场景在普通道路、高速公路、夜间、恶劣天气下的答案可能完全不同。靠整本纸质题海战术硬背很容易出现两种后果同一类题换个问法就做错错题没有记录下次还会在同一个坑里跌倒。于是我想自己写一个答题工具核心诉求只有三个随机出题、自动判分、错题回顾。随机出题能避免按顺序背题产生的“位置记忆”自动判分省去对答案的过程错题回顾把薄弱环节单独捞出来集中复习。技术选型上Python 3 自带sqlite3和json两个标准库不需要安装任何第三方依赖非常适合作为入门实战项目。2. 项目需求与功能设计2.1 功能清单功能说明题库导入从questions.json读取题目并写入 SQLite 数据库随机组卷每轮从题库中随机抽取 10 道题不重复答题交互用户输入 1-4 数字选择答案自动校验输入自动判分实时判断对错计算最终正确率错题记录答错的题目写入wrong_records表错题回顾展示最近 20 条错题带正确答案对比菜单循环支持反复练习直到用户主动退出2.2 技术选型组件选择语言Python 3.8数据库SQLite标准库sqlite3题库格式JSON 文件交互方式命令行不引入 Flask、Django 或其他 GUI 框架原因是项目核心价值在业务逻辑和数据设计上。命令行版本跑通之后再扩展成网页版或窗口程序会更顺理成章。2.3 项目结构subject1_quiz/ ├── db.py # 数据库连接、建表、题库导入、错题记录 ├── main.py # 主程序菜单、随机出题、答题交互、错题回顾 └── questions.json # 题库文件演示数据db.py负责所有数据层操作main.py负责业务逻辑和命令行交互题库独立成 JSON 文件。三层分离之后后续想换成 Excel 题库、接入远程 API都只需要改动db.py或新增一个导入模块不需要动主程序。3. 环境准备开始之前先确认本机 Python 环境。在终端执行python --version正常输出类似Python 3.10.12如果提示“python 不是内部或外部命令”在 Windows 上可以尝试py --versionmacOS 或 Linux 可以尝试python3 --version。本项目只需要 Python 标准库不需要pip install安装任何包。编辑器推荐 PyCharm、VS Code或者直接用系统自带的记事本也可以核心是能看懂报错信息、方便修改代码。然后创建项目目录mkdir subject1_quiz cd subject1_quiz后续所有代码文件都放在这个目录里。4. 题库与数据库设计4.1 为什么用数据库而不是直接读 JSON只读题库的话直接用json.load()加载文件完全够用。但加上“错题记录”之后纯 JSON 方案就会遇到麻烦错题需要追加、查询、按时间排序、统计错误次数这些操作用文件手写非常笨拙。SQLite 是 Python 内置支持的轻量级关系型数据库单文件存储不需要单独启动数据库服务。把题目和错题记录放进两张表查询和统计用 SQL 一行就能完成。4.2 表结构设计questions 表字段类型说明idINTEGER主键自增contentTEXT题目内容optionsTEXT选项列表JSON 字符串存储answerINTEGER正确答案下标从 0 开始categoryTEXT题目分类如信号灯、酒驾wrong_records 表字段类型说明idINTEGER主键自增question_idINTEGER外键指向 questions.iduser_answerINTEGER用户选择的错误答案下标created_atTEXT记录时间默认当前时间options用 JSON 字符串存储四个选项例如[加速通过, 停车等待, 减速观察后通过, 鸣喇叭提醒后通过]。这样表结构不用固定死选项数量以后想改成 3 个选项或 5 个选项也能兼容。answer使用从 0 开始的数字下标而不是直接存A/B/C/D。原因很简单代码里用user_answer q[answer]直接比较整数效率高、逻辑清晰展示给用户时再转成 1-4 编号。4.3 题库文件设计questions.json的格式如下{ questions: [ { content: 红灯亮时机动车应当如何通行, options: [加速通过, 停车等待, 减速观察后通过, 鸣喇叭提醒后通过], answer: 1, category: 信号灯 } ] }以下是完整的演示题库共 12 道题覆盖信号灯、证件、速度、酒驾、行人安全、高速公路、灯光、路口通行、处罚、事故处理、通行规则、特殊路面等常见分类。{ questions: [ { content: 红灯亮时机动车应当如何通行, options: [加速通过, 停车等待, 减速观察后通过, 鸣喇叭提醒后通过], answer: 1, category: 信号灯 }, { content: 驾驶机动车上道路行驶应当随车携带的证件是, options: [身份证和户口簿, 驾驶证和行驶证, 学生证, 居住证], answer: 1, category: 证件 }, { content: 高速公路对小型载客汽车的最高限速一般为多少, options: [80公里/小时, 100公里/小时, 120公里/小时, 140公里/小时], answer: 2, category: 速度 }, { content: 饮酒后驾驶机动车血液酒精含量达到多少属于醉酒驾驶, options: [20mg/100ml, 50mg/100ml, 80mg/100ml, 120mg/100ml], answer: 2, category: 酒驾 }, { content: 机动车行经人行横道遇到行人正在通过时应当, options: [加速通过, 鸣喇叭示意行人让行, 停车让行, 从行人后方绕行], answer: 2, category: 行人安全 }, { content: 在高速公路上行驶时下列哪种行为是被禁止的, options: [在服务区停车休息, 保持安全车距, 倒车或逆行, 按规定变更车道], answer: 2, category: 高速公路 }, { content: 夜间在照明不良的道路上行驶应当开启什么灯光, options: [关闭车灯, 前照灯, 只开启示廓灯, 危险报警闪光灯], answer: 1, category: 灯光 }, { content: 机动车通过没有交通信号灯控制的交叉路口时应当, options: [加速通过, 让右方道路来车先行, 让左方道路来车先行, 连续鸣喇叭通过], answer: 1, category: 路口通行 }, { content: 饮酒后驾驶机动车的暂扣机动车驾驶证多长时间, options: [1个月, 3个月, 6个月, 12个月], answer: 2, category: 处罚 }, { content: 造成交通事故后逃逸构成犯罪的驾驶证将被如何处理, options: [5年内不得重新取得, 10年内不得重新取得, 终生不得重新取得, 3年内不得重新取得], answer: 2, category: 事故处理 }, { content: 驾驶机动车遇到前方车辆排队等候时应当, options: [借用对向车道超越, 依次排队通行, 从右侧超车, 鸣喇叭催促], answer: 1, category: 通行规则 }, { content: 在冰雪道路上行驶以下做法正确的是, options: [提高车速快速通过, 保持原速行驶, 降低车速加大安全距离, 频繁变道绕行], answer: 2, category: 特殊路面 } ] }需要特别说明的是这份题库只是演示数据用来验证程序功能不是官方押题。实际备考请使用驾校指定的官方题库或者把官方练习软件中的题目整理成上述 JSON 格式后导入。5. 核心代码实现5.1 数据库模块 db.py文件路径subject1_quiz/db.py# 文件路径subject1_quiz/db.py import sqlite3 import json import os DB_PATH subject1_quiz.db DEFAULT_JSON questions.json def get_connection(): 获取 SQLite 连接。 return sqlite3.connect(DB_PATH) def init_database(json_pathDEFAULT_JSON): 初始化数据库。 步骤 1. 创建 questions 表 2. 创建 wrong_records 表 3. 从 JSON 题库文件导入不存在的题目。 conn get_connection() cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS questions ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL, options TEXT NOT NULL, answer INTEGER NOT NULL, category TEXT DEFAULT ) ) cursor.execute( CREATE TABLE IF NOT EXISTS wrong_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, question_id INTEGER NOT NULL, user_answer INTEGER NOT NULL, created_at TEXT DEFAULT (datetime(now, localtime)), FOREIGN KEY (question_id) REFERENCES questions(id) ) ) if os.path.exists(json_path): with open(json_path, encodingutf-8) as f: data json.load(f) count 0 for q in data.get(questions, []): content q[content] existing cursor.execute( SELECT id FROM questions WHERE content ?, (content,) ).fetchone() if existing is None: cursor.execute( INSERT INTO questions (content, options, answer, category) VALUES (?, ?, ?, ?), ( content, json.dumps(q[options], ensure_asciiFalse), q[answer], q.get(category, ), ), ) count 1 conn.commit() print(f题库初始化完成本次新增 {count} 道题) else: print(f提示题库文件 {json_path} 不存在已跳过自动导入。) conn.close() def load_questions(): 从数据库读取全部题目返回字典列表。 conn get_connection() rows conn.execute( SELECT id, content, options, answer, category FROM questions ).fetchall() conn.close() questions [] for row in rows: questions.append( { id: row[0], content: row[1], options: json.loads(row[2]),