Python: 实战:名片管理系统
这是 Phase 2 的结业项目。前几课学的列表、字典、推导式、字符串方法……全部用上,写一个真正能用的命令行名片管理系统。完成这个项目,你就真正掌握了 Python 容器的核心用法。
1. 项目需求
我们要做一个"名片管理器",在终端中运行,支持以下功能:
TEXT
📖 仅展示
1. 添加名片 —— 输入姓名、电话、邮箱
2. 显示所有名片 —— 以表格形式列出
3. 查询名片 —— 按姓名查找(支持模糊搜索)
4. 修改名片 —— 根据姓名修改信息
5. 删除名片 —— 根据姓名删除
6. 统计功能 —— 显示总人数、各邮箱域名统计
7. 退出程序
数据用列表+字典存储,所有名片放在一个列表中,每张名片是一个字典。
2. 数据结构设计
▶ 示例:名片数据结构
PYTHON
# 每张名片是一个字典
# 所有名片放在一个列表中
[
{"name": "Alice", "phone": "13800138000", "email": "zhangsan@example.com"},
{"name": "Bob", "phone": "13900139000", "email": "lisi@test.com"},
]
输出:
TEXT
📖 仅展示
# 执行成功
3. 完整代码
把这个文件保存为 card_manager.py 直接运行:
▶ 示例:名片管理系统
TEXT
📖 仅展示
# ====== 名片管理系统 ======
# 数据存储
cards = []
def show_menu():
"""显示主菜单"""
print("\n" + "=" * 35)
print("📇 名片管理系统")
print("=" * 35)
print("1. 添加名片")
print("2. 显示所有名片")
print("3. 查询名片")
print("4. 修改名片")
print("5. 删除名片")
print("6. 数据统计")
print("7. 退出")
print("=" * 35)
def add_card():
"""添加名片"""
print("\n--- 添加名片 ---")
name = input("姓名:").strip()
if not name:
print("姓名不能为空!")
return
phone = input("电话:").strip()
email = input("邮箱:").strip()
# 检查是否已存在同名名片
for card in cards:
if card["name"] == name:
print(f"已存在名为 {name} 的名片,请勿重复添加。")
return
cards.append({"name": name, "phone": phone, "email": email})
print(f"✅ 已添加 {name} 的名片。")
def list_cards():
"""显示所有名片"""
if not cards:
print("\n📭 目前没有名片记录。")
return
print("\n--- 所有名片 ---")
print(f"{'序号':<4}{'姓名':<8}{'电话':<15}{'邮箱':<25}")
print("-" * 52)
for i, card in enumerate(cards, 1):
print(f"{i:<4}{card['name']:<8}{card['phone']:<15}{card['email']:<25}")
print(f"\n共 {len(cards)} 张名片")
def search_card():
"""查询名片(支持模糊搜索)"""
if not cards:
print("\n📭 目前没有名片记录。")
return
keyword = input("\n请输入要查找的姓名(支持模糊搜索):").strip()
if not keyword:
print("请输入关键词!")
return
# 列表推导式 + in 实现模糊搜索
results = [card for card in cards if keyword in card["name"]]
if not results:
print(f"未找到包含 '{keyword}' 的名片。")
return
print(f"\n--- 找到 {len(results)} 张名片 ---")
for card in results:
print(f"姓名:{card['name']}")
print(f"电话:{card['phone']}")
print(f"邮箱:{card['email']}")
print("-" * 20)
def modify_card():
"""修改名片"""
if not cards:
print("\n📭 目前没有名片记录。")
return
name = input("\n请输入要修改的姓名:").strip()
for card in cards:
if card["name"] == name:
print(f"找到 {name} 的名片,请输入新信息(直接回车保持原值):")
new_name = input(f"姓名({card['name']}):").strip()
new_phone = input(f"电话({card['phone']}):").strip()
new_email = input(f"邮箱({card['email']}):").strip()
if new_name:
card["name"] = new_name
if new_phone:
card["phone"] = new_phone
if new_email:
card["email"] = new_email
print(f"✅ 已更新 {name} 的名片。")
return
print(f"未找到名为 {name} 的名片。")
def delete_card():
"""删除名片"""
if not cards:
print("\n📭 目前没有名片记录。")
return
name = input("\n请输入要删除的姓名:").strip()
for i, card in enumerate(cards):
if card["name"] == name:
confirm = input(f"确定要删除 {name} 的名片吗?(y/n):")
if confirm.lower() == "y":
cards.pop(i)
print(f"✅ 已删除 {name} 的名片。")
else:
print("已取消删除。")
return
print(f"未找到名为 {name} 的名片。")
def show_stats():
"""数据统计"""
if not cards:
print("\n📭 目前没有名片记录。")
return
print("\n--- 数据统计 ---")
print(f"名片总数:{len(cards)}")
# 统计邮箱域名
domains = {}
for card in cards:
email = card["email"]
if "@" in email:
domain = email.split("@")[1]
domains[domain] = domains.get(domain, 0) + 1
if domains:
print("\n邮箱域名统计:")
for domain, count in sorted(domains.items(), key=lambda x: x[1], reverse=True):
print(f" {domain}: {count} 人")
# 统计有电话和邮箱的人数
has_phone = sum(1 for card in cards if card["phone"])
has_email = sum(1 for card in cards if card["email"])
print(f"\n有电话的:{has_phone} 人")
print(f"有邮箱的:{has_email} 人")
# ====== 主程序 ======
print("🎉 欢迎使用名片管理系统!")
print("提示:输入对应数字选择功能。")
while True:
show_menu()
choice = input("请选择操作 (1-7):").strip()
if choice == "1":
add_card()
elif choice == "2":
list_cards()
elif choice == "3":
search_card()
elif choice == "4":
modify_card()
elif choice == "5":
delete_card()
elif choice == "6":
show_stats()
elif choice == "7":
print("感谢使用,再见!👋")
break
else:
print("无效选择,请输入 1-7 之间的数字。")
💡 代码设计思路: 每个功能用一个独立的函数实现,主程序用 while 循环 + if-elif 调度。这样代码结构清晰,增加新功能只需加一个新函数和一个 elif 分支。
4. 运行示例
TEXT
📖 仅展示
🎉 欢迎使用名片管理系统!
提示:输入对应数字选择功能。
===================================
📇 名片管理系统
===================================
1. 添加名片
2. 显示所有名片
3. 查询名片
4. 修改名片
5. 删除名片
6. 数据统计
7. 退出
===================================
请选择操作 (1-7):1
--- 添加名片 ---
姓名:Alice
电话:13800138000
邮箱:zhangsan@example.com
✅ 已添加 Alice 的名片。
请选择操作 (1-7):2
--- 所有名片 ---
序号 姓名 电话 邮箱
────────────────────────────────────────────────────
1 Alice 13800138000 zhangsan@example.com
共 1 张名片
▶ 示例:名片查找与统计(难度⭐⭐)
PYTHON
# 用列表推导式对名片列表做查找和统计
cards = [
{"name": "Alice", "phone": "13800138000", "email": "alice@example.com"},
{"name": "Bob", "phone": "13900139000", "email": "bob@test.com"},
{"name": "Alice Chen", "phone": "13700137000", "email": "alice.c@work.com"},
]
# 模糊搜索:名字中包含 "Alice"
results = [c for c in cards if "Alice" in c["name"]]
print(f"找到 {len(results)} 个 Alice:")
for c in results:
print(f" {c['name']} - {c['phone']}")
# 统计各邮箱域名
domains = {}
for c in cards:
if "@" in c["email"]:
domain = c["email"].split("@")[1]
domains[domain] = domains.get(domain, 0) + 1
print("\n邮箱域名统计:")
for d, count in sorted(domains.items(), key=lambda x: x[1], reverse=True):
print(f" {d}: {count} 人")
# 检查是否有重复电话
phones = [c["phone"] for c in cards]
has_dup = len(phones) != len(set(phones))
print(f"\n是否有重复电话:{has_dup}")
输出:
TEXT
📖 仅展示
找到 2 个 Alice:
Alice - 13800138000
Alice Chen - 13700137000
邮箱域名统计:
example.com: 1 人
test.com: 1 人
work.com: 1 人
是否有重复电话:False
5. 扩展建议
项目完成后可以自己尝试加入这些功能:
| 功能 | 实现思路 |
|---|---|
| 数据持久化 | 用 json.dump() 保存到文件,程序启动时用 json.load() 读取(后面会学文件操作) |
| 分组管理 | 给名片加一个 group 字段(同事/朋友/家人),用字典按组分门别类 |
| 数据导入导出 | 用 join() 输出 CSV 文件,用 Excel 打开 |
| 排序显示 | 按姓名排序:用 sorted(cards, key=lambda c: c["name"]) |
| 批量删除 | 输入多个姓名用逗号分隔,循环删除 |
❓ 常见问题
Q 为什么用列表+字典而不是直接操作文件?
A 这个项目聚焦于容器操作——列表存所有名片,字典存单张名片。这样你能练习列表的增删改查和字典的键值操作。文件读写(持久化)将在 Phase 3 学习。先练好容器,再学文件,循序渐进。
Q 程序关闭后数据就丢了,怎么办?
A 这是正常的——因为我们把数据存在内存中,程序结束就释放了。要持久化数据,后面学了文件操作和 JSON 模块后,可以用
json.dump(cards, open("data.json", "w")) 保存到硬盘。Q 模糊搜索用了
in 关键字,它是区分大小写的吗?A 是的。
"张" in "Alice" 是 True,但 "zhang" in "Zhang" 是 False(大小写敏感)。如果你的搜索需要忽略大小写,可以统一转成小写再比较:keyword.lower() in card["name"].lower()。📖 小节
- 名片管理系统用了列表存所有数据,字典存单条记录——这是最常用的数据组织方式
- 模块化设计:每个功能一个独立函数,主程序用循环调度
enumerate()生成带序号的列表显示- 列表推导式实现模糊搜索
- 字典的
get()方法用于频率统计 - 项目虽然简单,但涵盖了 Phase 2 全部核心知识点
📝 作业
-
基础题(难度⭐):给名片系统加一个新功能——按姓名排序显示(使用
sorted()的key参数)。 -
进阶题(难度⭐⭐):给名片系统加一个"分组管理"功能。每张名片增加
group字段("同事"、"朋友"、"家人"),然后实现按组查看名片的菜单项。 -
挑战题(难度⭐⭐⭐):自己从头写一个"图书管理系统",不参考上面的代码。功能包括:添加图书(书名、作者、年份)、显示全部、按作者搜索、按年份排序、统计各作者图书数量。用列表+字典存储,全部在命令行运行。