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()

📖 小节


📝 作业

  1. 基础题(难度⭐):给名片系统加一个新功能——按姓名排序显示(使用 sorted()key 参数)。

  2. 进阶题(难度⭐⭐):给名片系统加一个"分组管理"功能。每张名片增加 group 字段("同事"、"朋友"、"家人"),然后实现按组查看名片的菜单项。

  3. 挑战题(难度⭐⭐⭐):自己从头写一个"图书管理系统",不参考上面的代码。功能包括:添加图书(书名、作者、年份)、显示全部、按作者搜索、按年份排序、统计各作者图书数量。用列表+字典存储,全部在命令行运行。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏