
本文详解为何 view_statistics() 总提示“No questions available”,并系统性修复 CSV 读写逻辑、类属性初始化、ID 分配及类型字段缺失等核心缺陷,确保统计功能正确加载已保存题目。
本文详解为何 `view_statistics()` 总提示“no questions available”,并系统性修复 csv 读写逻辑、类属性初始化、id 分配及类型字段缺失等核心缺陷,确保统计功能正确加载已保存题目。
在构建交互式学习工具时,view_statistics() 函数始终输出 “No questions available”,表面看是数据为空,实则根源在于数据持久化与反序列化流程存在多处关键断裂。以下为完整修复方案:
? 核心问题诊断与修复
✅ 1. questions.csv 未被正确写入或读取
原 save_questions() 使用 question.__dict__.copy() 导出字段,但 Quiz 和 Freeform 实例缺少 id、type 等关键属性(如 id 在 __init__ 中未初始化),导致 CSV 写入缺失必要列;而 load_questions() 又依赖这些字段解析,形成死循环。
修复方式:统一初始化所有字段,并显式声明 id 和 type
# 修改 questions.py
class Question:
def __init__(self, question, answer, enabled=True, times_shown=0, correct_count=0):
self.question = question
self.answer = answer
self.enabled = enabled
self.times_shown = times_shown
self.correct_count = correct_count
self.id = None # 显式初始化,避免 __dict__ 缺失
self.type = None # 同上,供 save/load 识别类型
class Quiz(Question):
def __init__(self, question, choices, correct_answer, enabled=True, times_shown=0, correct_count=0):
super().__init__(question, correct_answer, enabled, times_shown, correct_count)
self.choices = choices
self.type = 'quiz' # 强制设 type,避免 None
class Freeform(Question):
def __init__(self, question, answer, enabled=True, times_shown=0, correct_count=0):
super().__init__(question, answer, enabled, times_shown, correct_count)
self.type = 'freeform'✅ 2. save_questions() 字段不匹配 load_questions() 解析逻辑
原 csv.DictWriter 的 fieldnames 缺少 id,且 choices 存储为字符串(', '.join(...)),但 load_questions() 直接用 row['choices'].split(', ') 解析——若原始 choice 含空格或逗号,将解析错误。
立即学习“Python免费学习笔记(深入)”;
修复方式:使用 JSON 序列化 choices,并补全 id 字段
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
import json
def save_questions(questions, file_name="questions.csv"):
with open(file_name, "w", newline='', encoding='utf-8') as file:
# 补全 id 字段 & choices 改用 JSON 安全序列化
fieldnames = ["id", "question", "type", "choices", "answer", "enabled", "times_shown", "correct_count"]
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for i, q in enumerate(questions, 1):
row = q.__dict__.copy()
row['id'] = i # 动态分配 ID(或从 q.id 读取,需确保 q.id 已赋值)
if isinstance(q, Quiz):
row['choices'] = json.dumps(q.choices, ensure_ascii=False)
else:
row['choices'] = ""
writer.writerow(row)✅ 3. load_questions() 解析逻辑健壮性不足
原代码未处理 id 字段缺失、choices JSON 解析异常、answer 类型混淆(Quiz 的 answer 是 int 索引,Freeform 是 str)等问题。
修复方式:增强容错 + 正确还原对象结构
def load_questions(file_name="questions.csv"):
if not os.path.exists(file_name):
print("No questions found. The file does not exist.")
return []
questions = []
try:
with open(file_name, encoding='utf-8') as file:
reader = csv.DictReader(file)
for row in reader:
try:
# 安全解析基础字段
q_id = int(row.get('id', 0))
q_type = row.get('type', '').strip()
question_text = row.get('question', '').strip()
answer_raw = row.get('answer', '')
enabled = row.get('enabled', 'True').lower() == 'true'
times_shown = int(row.get('times_shown', 0))
correct_count = int(row.get('correct_count', 0))
if not question_text:
continue
if q_type == 'quiz':
try:
choices = json.loads(row.get('choices', '[]'))
# answer 应为整数索引(原保存时是 int)
correct_idx = int(answer_raw) if answer_raw.isdigit() else 0
q = Quiz(question_text, choices, correct_idx, enabled, times_shown, correct_count)
q.id = q_id
questions.append(q)
except (json.JSONDecodeError, ValueError) as e:
print(Fore.YELLOW + f"Skip invalid quiz row {q_id}: {e}" + Style.RESET_ALL)
continue
elif q_type == 'freeform':
q = Freeform(question_text, answer_raw, enabled, times_shown, correct_count)
q.id = q_id
questions.append(q)
except Exception as e:
print(Fore.RED + f"Error loading question {row.get('id', 'unknown')}: {e}" + Style.RESET_ALL)
continue
except Exception as e:
print(Fore.RED + f"Failed to read {file_name}: {e}" + Style.RESET_ALL)
return questions✅ 4. view_statistics() 中的计算与显示错误
原代码中 Correct Percentage 计算错误(误用 question.correct_count % 而非百分比公式),且未校验 times_shown > 0 导致除零风险。
修复方式:修正公式 + 增加安全判断
def view_statistics(questions):
if not questions:
print(Fore.RED + "No questions available" + Style.RESET_ALL)
return
print("Question Statistics:")
print("===============================================")
for question in questions:
# ✅ 修复百分比计算:correct_count / times_shown * 100,且 times_shown > 0
correct_percentage = (
(question.correct_count / question.times_shown * 100)
if question.times_shown > 0 else 0.0
)
status = Fore.GREEN + "Enabled" + Style.RESET_ALL if question.enabled else Fore.RED + "Disabled" + Style.RESET_ALL
print(f"ID: {question.id} | Status: {status}")
print(f"Type: {question.type}")
print(f"Question: {question.question}")
print(f"Times Shown: {question.times_shown}")
print(f"Correct Count: {question.correct_count}")
print(f"Correct Percentage: {correct_percentage:.1f}%")
print("===============================================")⚠️ 关键注意事项
-
文件路径一致性:确保
questions.csv与主脚本在同一目录,或使用os.path.join(os.path.dirname(__file__), "questions.csv")构建绝对路径。 -
首次运行需先添加题目:
view_statistics()依赖load_questions(),而后者只读取已有 CSV —— 必须先执行(a) Adding questions并成功保存后,再选(b)才能显示数据。 -
调试建议:在
save_questions()后添加print(f"Saved {len(questions)} questions to {file_name}");在load_questions()开头打印os.path.abspath(file_name)验证路径。
完成以上修改后,添加题目 → 保存 → 切换至统计模式,即可稳定显示结构化统计数据。核心原则是:写入字段必须与读取逻辑严格对齐,对象属性必须全程显式初始化,关键数值运算必须防御性校验。

















