十二. 实战项目一:通讯录系统(文件版)
这是一个功能完整的命令行通讯录,支持增删改查、模糊搜索、JSON 持久化。
import json
import os
class Contact:
def __init__(self, name, phone, email=""):
self.name = name
self.phone = phone
self.email = email
def to_dict(self):
return {"name": self.name, "phone": self.phone, "email": self.email}
@staticmethod
def from_dict(data):
return Contact(data["name"], data["phone"], data.get("email", ""))
def __str__(self):
return f"{self.name} | {self.phone} | {self.email}"
class AddressBook:
def __init__(self, filename="contacts.json"):
self.filename = filename
self.contacts = [] # 存储 Contact 对象
self.load()
def load(self):
if not os.path.exists(self.filename):
return
try:
with open(self.filename, "r", encoding="utf-8") as f:
data = json.load(f)
self.contacts = [Contact.from_dict(item) for item in data]
print(f"加载了 {len(self.contacts)} 个联系人")
except Exception as e:
print(f"加载失败: {e}")
def save(self):
with open(self.filename, "w", encoding="utf-8") as f:
json.dump([c.to_dict() for c in self.contacts], f, ensure_ascii=False, indent=2)
print("已保存")
def add(self, name, phone, email=""):
# 检查重复
if any(c.name == name for c in self.contacts):
print(f"联系人 {name} 已存在")
return False
self.contacts.append(Contact(name, phone, email))
self.save()
print(f"添加成功: {name}")
return True
def delete(self, name):
for i, c in enumerate(self.contacts):
if c.name == name:
del self.contacts[i]
self.save()
print(f"已删除 {name}")
return True
print(f"未找到 {name}")
return False
def search(self, keyword):
"""支持模糊搜索(不区分大小写)"""
keyword_lower = keyword.lower()
results = [c for c in self.contacts if keyword_lower in c.name.lower() or keyword_lower in c.phone]
if not results:
print("无匹配结果")
else:
print(f"找到 {len(results)} 个联系人:")
for c in results:
print(c)
return results
def list_all(self):
if not self.contacts:
print("通讯录为空")
return
print("\n===== 通讯录 =====")
for c in sorted(self.contacts, key=lambda x: x.name):
print(c)
print(f"共 {len(self.contacts)} 人\n")
def main():
book = AddressBook()
while True:
print("\n1.添加 2.删除 3.搜索 4.显示全部 5.保存 0.退出")
choice = input("请选择: ").strip()
if choice == "1":
name = input("姓名: ").strip()
phone = input("电话: ").strip()
email = input("邮箱(可选): ").strip()
book.add(name, phone, email)
elif choice == "2":
name = input("要删除的姓名: ").strip()
book.delete(name)
elif choice == "3":
kw = input("搜索关键字: ").strip()
book.search(kw)
elif choice == "4":
book.list_all()
elif choice == "5":
book.save()
elif choice == "0":
book.save()
print("再见")
break
else:
print("无效选项")
if __name__ == "__main__":
main()
项目亮点:
使用 JSON 持久化,人类可读。
面向对象分离 Contact 和 AddressBook 职责。
模糊搜索、重复检查、自动排序显示。
代码约 100 行,非常适合初学者阅读和扩展。
十三. 实战项目二:学生成绩管理系统
使用 SQLite 数据库替代文件存储,支持复杂查询、统计分析、成绩报表导出 CSV。
13.1 准备工作
SQLite 是 Python 内置数据库,无需安装。
import sqlite3
import csv
from datetime import datetime
class StudentDB:
def __init__(self, db_name="students.db"):
self.conn = sqlite3.connect(db_name)
self.cursor = self.conn.cursor()
self._create_table()
def _create_table(self):
self.cursor.execute("""
CREATE TABLE IF NOT EXISTS students (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
score REAL NOT NULL,
class_name TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
self.conn.commit()
def add_student(self, name, score, class_name=None):
try:
self.cursor.execute(
"INSERT INTO students (name, score, class_name) VALUES (?, ?, ?)",
(name, score, class_name)
)
self.conn.commit()
print(f"添加成功: {name}")
return True
except sqlite3.IntegrityError:
print(f"学生 {name} 已存在")
return False
def delete_student(self, name):
self.cursor.execute("DELETE FROM students WHERE name = ?", (name,))
self.conn.commit()
if self.cursor.rowcount > 0:
print(f"已删除 {name}")
else:
print(f"未找到 {name}")
def update_score(self, name, new_score):
self.cursor.execute("UPDATE students SET score = ? WHERE name = ?", (new_score, name))
self.conn.commit()
if self.cursor.rowcount > 0:
print(f"{name} 成绩已更新为 {new_score}")
else:
print(f"未找到 {name}")
def query(self, name):
self.cursor.execute("SELECT * FROM students WHERE name = ?", (name,))
row = self.cursor.fetchone()
if row:
print(f"ID: {row[0]}, 姓名: {row[1]}, 成绩: {row[2]}, 班级: {row[3]}, 创建时间: {row[4]}")
else:
print("未找到")
def show_all(self, order_by="score DESC"):
self.cursor.execute(f"SELECT name, score, class_name FROM students ORDER BY {order_by}")
rows = self.cursor.fetchall()
if not rows:
print("暂无数据")
return
print("\n===== 学生成绩表 =====")
print(f"{'姓名':<10} {'成绩':<8} {'班级':<10}")
print("-" * 30)
for name, score, cls in rows:
print(f"{name:<10} {score:<8} {cls if cls else '':<10}")
print(f"共 {len(rows)} 条记录\n")
def statistics(self):
self.cursor.execute("SELECT COUNT(*), AVG(score), MAX(score), MIN(score) FROM students")
count, avg, max_score, min_score = self.cursor.fetchone()
if count == 0:
print("无数据")
return
print(f"学生人数: {count}")
print(f"平均分: {avg:.2f}")
print(f"最高分: {max_score}")
print(f"最低分: {min_score}")
# 查询最高分学生
self.cursor.execute("SELECT name FROM students WHERE score = ?", (max_score,))
top_students = [row[0] for row in self.cursor.fetchall()]
print(f"最高分学生: {', '.join(top_students)}")
def export_csv(self, filename="report.csv"):
self.cursor.execute("SELECT name, score, class_name, created_at FROM students ORDER BY score DESC")
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["姓名", "成绩", "班级", "创建时间"])
writer.writerows(self.cursor.fetchall())
print(f"报表已导出到 {filename}")
def close(self):
self.conn.close()
# 命令行交互部分与之前类似,此处省略,请自行扩展
十四. 调试与测试
14.1 使用 print 调试
def calculate(nums):
print(f"[DEBUG] nums: {nums}") # 打印变量
total = sum(nums)
print(f"[DEBUG] total: {total}")
return total
14.2 使用 pdb 断点调试
import pdb
def buggy_function(x):
pdb.set_trace() # 程序在此暂停
result = x / 0
return result
常用命令:
n (next): 执行下一行
s (step): 进入函数内部
c (continue): 继续运行直到下一个断点
p 变量 (print): 打印变量值
q (quit): 退出
14.3 单元测试(unittest)
import unittest
def add(a, b):
return a + b
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
self.assertEqual(add(-1, 1), 0)
def test_add_negative(self):
self.assertLess(add(-5, -2), 0)
if __name__ == "__main__":
unittest.main()
14.4 doctest(文档测试)
def multiply(a, b):
"""
返回两数乘积。
>>> multiply(3, 4)
12
>>> multiply(-1, 5)
-5
"""
return a * b
if __name__ == "__main__":
import doctest
doctest.testmod()