第十九课:你的第一个小程序 – 综合项目实战 (简易通讯录)

  • 时长: 90 分钟
  • 教学目标:
    • 综合运用学过的所有核心知识:变量、数据类型、列表、字典、函数、循环、条件判断、文件操作、错误处理。
    • 学习分析一个完整的应用需求,并将其分解为更小的功能模块。
    • 练习使用函数来组织不同的功能代码,使程序结构清晰。
    • 练习使用字典来存储有关联的数据(姓名和电话号码)。
    • 练习使用文件操作来保存和加载数据,让信息不会丢失。
    • 练习使用错误处理来提高程序的健壮性,应对用户输入错误或文件不存在等情况。
    • 提升独立分析问题和编写完整应用程序的能力。
    • 体验从零开始构建一个实际应用的过程和乐趣。

课程内容

1. 复习与引入 (10 分钟)

  • 复习: 快速回顾:函数的作用(打包代码)、列表(有序)、字典(键值对)、文件操作(读写保存)、错误处理(try...except)。
  • 提出问题: 老师提问:“我们已经学会了这么多编程技巧。如果现在我想写一个程序,能帮我记住朋友们的名字和电话号码,并且下次打开程序时,之前记下的信息还在,不会丢失,该怎么做呢?这个程序应该能添加新朋友,能查找某个朋友的电话,还能显示所有朋友的列表。”
  • 引出综合项目: 这就是一个小型应用程序的例子。它需要存储数据、进行各种操作,还需要把数据保存到硬盘上。解决这样的问题,就需要把我们之前学过的所有知识都综合起来运用。今天,我们就来一起完成一个“简易通讯录”小程序。

2. 项目需求分析与功能分解 (15 分钟)

  • 明确目标: 开发一个能存储姓名和电话号码,并支持添加、查找、显示、保存、加载功能的简易通讯录程序。
  • 数据结构选择: 如何存储姓名和电话号码?姓名是唯一的,电话号码与姓名关联。用字典最合适!键是姓名,值是电话号码。整个通讯录就是这个字典。
    # 我们的通讯录就存放在这个字典里
    # 例如: contacts = {"小明": "13812345678", "小红": "13987654321"}
    contacts = {} # 程序开始时,通讯录通常是空的,或者从文件加载
    
  • 功能分解: 程序需要提供哪些操作?
    • 加载数据: 启动时从文件读取之前保存的联系人信息。
    • 添加联系人: 获取姓名和号码,存到字典里。
    • 查找联系人: 获取姓名,从字典里查找并显示号码。
    • 显示所有联系人: 遍历字典,打印所有姓名和号码。
    • 保存数据: 将字典里的联系人信息写到文件里。
    • 主菜单: 显示操作选项,根据用户输入调用相应的功能。
    • 退出程序: 停止运行。
  • 函数设计: 为每个功能编写一个独立的函数,让代码更整洁、易读。
    • load_contacts(filename): 从文件加载数据,返回一个字典。
    • save_contacts(contacts, filename): 将字典数据保存到文件。
    • add_contact(contacts): 添加新的联系人。
    • find_contact(contacts): 查找联系人。
    • list_contacts(contacts): 显示所有联系人。
    • main_menu(): 显示菜单,获取用户选择,调用其他函数,控制程序运行流程。
  • 文件选择: 数据保存到哪个文件?可以约定一个文件名,比如 contacts.txt。文件格式可以简单约定,比如每行是一个联系人,姓名和号码之间用逗号分隔:姓名,电话\n

3. Step by Step 实现项目代码 (40 分钟)

老师带领学生一步一步编写代码。每实现一个函数,都进行简单的测试。

  • 第一步:程序框架和主菜单 main_menu()
    # 约定保存数据的文件名
    CONTACT_FILE = "contacts.txt"
    
    # 主程序入口函数
    def main_menu():
        # 1. 启动时加载数据
        contact_book = load_contacts(CONTACT_FILE)
        print("通讯录已加载。")
    
        # 2. 进入主循环
        while True:
            print("\n--- 通讯录菜单 ---")
            print("1. 添加联系人")
            print("2. 查找联系人")
            print("3. 显示所有联系人")
            print("4. 保存通讯录")
            print("5. 退出")
            print("-" * 20)
    
            choice = input("请输入你的选择 (1-5): ")
    
            # 3. 根据用户选择调用不同的功能
            if choice == '1':
                add_contact(contact_book)
            elif choice == '2':
                find_contact(contact_book)
            elif choice == '3':
                list_contacts(contact_book)
            elif choice == '4':
                save_contacts(contact_book, CONTACT_FILE)
                print("通讯录已保存到文件。")
            elif choice == '5':
                print("正在退出通讯录...")
                save_contacts(contact_book, CONTACT_FILE) # 退出前保存一次
                print("通讯录已保存并安全退出。")
                break # 退出 while 循环,程序结束
            else:
                print("无效的选择,请重新输入。")
    
    # 在程序的最下面,调用主菜单函数启动程序
    # if __name__ == "__main__": # 这句话的意思是:如果当前文件是主程序,就执行下面的代码
    # 这样写是为了防止这个文件被当作模块导入时,main_menu() 被意外执行
    main_menu()
    

    (讲解主循环 while True,菜单打印,用户输入,if-elif-else 分支,break 退出循环。先只写框架,功能函数后面再实现)

  • 第二步:加载联系人 load_contacts()

    # 加载联系人函数
    def load_contacts(filename):
        contacts = {} # 初始化一个空的字典来存放联系人
        try:
            # 尝试以读取模式打开文件
            with open(filename, 'r', encoding='utf-8') as f:
                # 逐行读取文件内容
                for line in f:
                    # 去掉行末的空白字符(包括换行符)
                    line = line.strip()
                    # 如果这一行不是空的
                    if line:
                        # 按逗号分割姓名和电话
                        parts = line.split(',')
                        # 确保分割后至少有两部分 (姓名和电话)
                        if len(parts) >= 2:
                            name = parts[0]
                            phone = parts[1]
                            contacts[name] = phone # 存储到字典中
                            print(f"加载联系人: {name} - {phone}") # 可选,加载时打印提示
    
        except FileNotFoundError:
            # 如果文件不存在,这是第一次运行,打印提示,返回空字典
            print(f"文件 {filename} 未找到,将创建一个新的通讯录。")
        except Exception as e: # 捕捉其他可能的错误
            print(f"加载文件时发生错误: {e}")
    
        return contacts # 返回加载好的通讯录字典
    

    (讲解 with open('r')try...except FileNotFoundError,文件逐行遍历,.strip() 清除空白,.split(',') 分割字符串,以及如何存入字典。强调错误处理。)

  • 第三步:保存联系人 save_contacts()

    # 保存联系人函数
    def save_contacts(contacts, filename):
        try:
            # 尝试以写入模式打开文件 (会清空原有内容再写入)
            with open(filename, 'w', encoding='utf-8') as f:
                # 遍历通讯录字典的键值对
                for name, phone in contacts.items():
                    # 将每个联系人格式化成 "姓名,电话" 的字符串,并加上换行符
                    line = f"{name},{phone}\n"
                    f.write(line) # 写入文件
        except Exception as e: # 捕捉写入时可能发生的错误
            print(f"保存文件时发生错误: {e}")
    

    (讲解 with open('w'),遍历字典的 .items(),f-string 格式化字符串,手动加 \n,以及错误处理。)

  • 第四步:添加联系人 add_contact()

    # 添加联系人函数
    def add_contact(contacts):
        print("\n--- 添加新的联系人 ---")
        name = input("请输入姓名: ")
        # 可以加一个简单的判断,姓名不能为空
        if not name:
            print("姓名不能为空!")
            return # 结束函数
    
        phone = input("请输入电话号码: ")
        # 可以加一个简单的判断,电话号码不能为空
        if not phone:
            print("电话号码不能为空!")
            return # 结束函数
    
        # 检查姓名是否已经存在 (可选)
        if name in contacts:
            print(f"联系人 {name} 已经存在,号码是 {contacts[name]}。")
            overwrite_choice = input("是否覆盖原有号码?(y/n): ").lower()
            if overwrite_choice != 'y':
                print("取消添加。")
                return # 结束函数
    
        # 将新的键值对添加到字典中
        contacts[name] = phone
        print(f"联系人 {name} 已添加/更新成功!")
    

    (讲解如何获取用户输入,如何添加到字典。可以引入简单的输入验证和覆盖检查。)

  • 第五步:查找联系人 find_contact()

    # 查找联系人函数
    def find_contact(contacts):
        print("\n--- 查找联系人 ---")
        name_to_find = input("请输入要查找的姓名: ")
    
        # 使用 .get() 方法安全地查找,如果找不到返回 None
        phone_number = contacts.get(name_to_find)
    
        if phone_number is not None: # 如果 .get() 返回的不是 None,说明找到了
            print(f"找到联系人:{name_to_find} 的电话是 {phone_number}")
        else: # 如果 .get() 返回 None,说明没找到
            print(f"抱歉,通讯录中没有找到联系人:{name_to_find}")
    

    (讲解如何获取用户输入,如何使用 .get() 安全查找,以及根据查找结果(是否为 None)使用 if-else 打印不同信息。)

  • 第六步:显示所有联系人 list_contacts()

    # 显示所有联系人函数
    def list_contacts(contacts):
        print("\n--- 所有联系人 ---")
        # 检查字典是否为空
        if not contacts: # 或者 len(contacts) == 0
            print("通讯录是空的。")
            return # 结束函数
    
        # 遍历字典的键值对并打印
        for name, phone in contacts.items():
            print(f"姓名: {name}, 电话: {phone}")
        print("-" * 20) # 打印分隔线
    

    (讲解如何检查字典是否为空,以及如何遍历字典的 .items() 并打印键值对。)

4. 整合、测试与完善 (10 分钟)

  • 回顾整体结构: 再次展示完整的代码结构,强调函数之间的调用关系(main_menu 调用其他功能函数,main_menu 启动程序)。
  • 测试: 带领学生一起运行完整的程序,进行各种测试:
    • 第一次运行程序(文件不存在)。
    • 添加几个联系人。
    • 显示所有联系人。
    • 查找存在的联系人和不存在的联系人。
    • 保存通讯录。
    • 退出程序。
    • 再次运行程序,检查之前保存的联系人是否被加载。
    • 尝试输入无效的菜单选项。
    • (如果时间允许)尝试手动修改 contacts.txt 文件,比如删除一行或添加一行格式错误的,再运行程序看加载是否出错(如果加了错误处理的话)。
  • 完善: 根据测试中发现的问题进行简单调整(比如提示信息更友好,增加输入验证等)。

5. 总结与提问 (5 分钟)

  • 回顾: 总结本节课完成了一个简单的通讯录小程序。回顾在项目中综合运用了哪些编程概念(列表、字典、函数、循环、条件、文件、错误处理)。强调了分解问题、函数封装、数据持久化的重要性。
  • 提问:
    • 我们为什么选择用字典来存储联系人?
    • 哪个函数负责把数据保存到文件?哪个函数负责从文件加载?
    • 在加载文件时,我们使用了 try...except 来处理什么错误?
    • 主菜单的循环是什么类型的循环?为什么用它?
    • 如果我想删除一个联系人,应该修改哪个函数?
  • 答疑: 回答学生问题。

6. 布置课后作业 (5 分钟)

  • 作业 1: 在课堂完成的通讯录程序基础上,添加一个“删除联系人”的功能(在主菜单中增加一个选项,并编写相应的函数 delete_contact(contacts))。这个函数需要询问用户要删除的姓名,然后在字典中使用 del contacts[姓名].pop(姓名) 进行删除,并考虑姓名不存在的情况(使用 in 检查或 try...except KeyError)。
  • 作业 2: 扩展“查找联系人”功能,除了根据姓名查找,还可以根据电话号码反向查找姓名(这可能需要遍历字典的所有键值对)。
  • 作业 3: (选做) 修改文件保存格式,比如使用 JSON 格式(简单介绍 import json 模块的 json.dump()json.load()),让数据保存和加载更规范。
  • 作业 4: (选做) 统计通讯录中有多少个联系人,并在主菜单中显示总数。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。