Python通讯录项目中如何避免JSON文件被覆盖?
解决通讯录管理器JSON数据覆盖及加载失效问题
问题根源分析
- 数据加载未关联到通讯录实例:调用
load_file后未将读取的JSON数据转为Contact对象并添加到ContactBook的联系人列表,导致每次启动都是空通讯录,退出时新数据直接覆盖原有数据。 Contact类序列化结构冗余:__str__方法返回嵌套的contact_info结构,导致最终保存的JSON出现不必要的层级嵌套。ContactBook的contacts为类属性:所有ContactBook实例共享同一个联系人列表,易引发数据混乱。- 删除操作存在潜在bug:搜索不到联系人时
search_contact返回提示字符串,直接传入remove_contact会触发报错。
修复步骤及代码修改
1. 修复数据加载逻辑
修改main函数,将加载的JSON数据转化为Contact对象并初始化通讯录:
from colorama import Fore, Style from LoadAndSave import load_file, save_file from ContactBook import ContactBook from Contact import Contact DATA_FILE = "contactbook.json" def menu(): print(Fore.RED + "Welcome to Contacts Manager v2.0.0!" + Style.RESET_ALL) print(Fore.LIGHTCYAN_EX + "Please enter your selection according to the menu." + Style.RESET_ALL) print("a - Add a contact") print("r - Remove a contact") print("s - Search for a contact") print("p - Print a list of all contacts") print("x - Exit") user_selection = input(Fore.GREEN + "Your selection: " + Style.RESET_ALL) return user_selection def main(): # 加载数据并转化为Contact对象 loaded_data = load_file(DATA_FILE) contactbook = ContactBook() # 兼容现有JSON结构,将数据转为Contact实例 for item in loaded_data: if isinstance(item, dict) and "contact_info" in item: for contact_data in item["contact_info"]: contactbook.add_contact(contact_data["name"], contact_data["tel"]) elif isinstance(item, dict) and "name" in item and "tel" in item: contactbook.add_contact(item["name"], item["tel"]) user_selection = "" while user_selection != "x": user_selection = menu() if user_selection == "a": contactbook.add_contact(input("Please enter contact's name: "), int(input("Please enter contact's number: "))) if user_selection == "r": target_contact = contactbook.search_contact(input("Contact's Name? ")) # 仅找到联系人时执行删除 if isinstance(target_contact, Contact): contactbook.remove_contact(target_contact) else: print(target_contact) if user_selection == "s": result = contactbook.search_contact(input("Contact's name?")) print(result) print(Fore.GREEN + "Success!" + Style.RESET_ALL) if user_selection == "p": print(contactbook) print(Fore.GREEN + "Success!" + Style.RESET_ALL) print(Fore.MAGENTA + "Thank you for using my software. Bye!") save_file(contactbook.make_contact_json(), DATA_FILE) if __name__ == "__main__": main()
2. 优化Contact类结构
简化序列化逻辑,新增to_dict方法避免字符串解析:
import json class Contact: def __init__(self, name="", tel=0): self.name = name self.tel = tel def __str__(self): return json.dumps({"name": self.name, "tel": self.tel}) def to_dict(self): return {"name": self.name, "tel": self.tel}
3. 修复ContactBook类属性及方法
将contacts改为实例属性,优化序列化方法:
from colorama import Fore, Style, Back from Contact import Contact import json class ContactBook: def __init__(self): # 改为实例属性,每个通讯录实例拥有独立列表 self.contacts = [] def add_contact(self, name="", tel=0): self.contacts.append(Contact(name, tel)) print(Fore.GREEN + "Success!" + Style.RESET_ALL) def remove_contact(self, contact): if isinstance(contact, Contact): self.contacts.remove(contact) print(Fore.RED + "Removed.\n" + Style.RESET_ALL) def search_contact(self, contact_name): for contact in self.contacts: if contact.name == contact_name: return contact return Back.RED + "\nThere is no contact with that name." + Style.RESET_ALL + "\n" def make_contact_json(self): # 直接使用to_dict生成序列化数据 return [contact.to_dict() for contact in self.contacts] def __str__(self) -> str: result = "" for contact in self.contacts: result += str(contact) + "\n" return result
4. 优化后JSON结构
修复后保存的JSON会更简洁,避免嵌套冗余:
[ { "name": "sdfgh", "tel": 654321 }, { "name": "new_contact", "tel": 123456 } ]
关键修复点总结
- 确保加载的数据被正确转为
Contact对象并添加到通讯录实例 - 将类属性改为实例属性,避免跨实例数据共享
- 简化序列化结构,减少不必要的层级嵌套
- 增加删除操作的类型判断,避免无效输入报错
内容的提问来源于stack exchange,提问作者Alon Itzhaky
相关产品推荐
相关产品推荐

