You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

面向对象地址簿程序删除条目异常问题求助

问题分析

你遇到的核心问题是显示的条目是排序后的,但删除操作基于原列表的顺序,两者索引不匹配,导致删除了错误的元素;同时删除方法的异常处理逻辑存在漏洞,没有正确检查索引有效性。

举个例子:原列表顺序是[Joe J, Eric, Joe S],但界面显示的是排序后的[Eric, Joe J, Joe S],你输入1想删Eric,实际删除的是原列表第0位的Joe J,再次显示时排序后的列表就只剩Eric和Joe S,和预期完全不符。

修正后的完整代码

import pickle
import os.path

# Constants
SAVE_FILE_NAME = 'address_book_contents.pickle'
INSTRUCTIONS = '''Address Book Application
Press:
a to add an entry
r to remove an entry
d to display a list of all entries in summary form
s to display a list of all entries sorted alphabetically in summary form
i to print these instructions again
q to quit
'''
CONFIRM_QUIT_MESSAGE = 'Are you sure you want to quit (Y/n)? '
SUMMARY_TEMPLATE = "%s %s DOB: %s email: %s"

### Classes Section
class AddressBook (object):
    ''' This class holds and manages a list of my contacts '''
    def __init__ (self):
        ''' Set people attribute to an empty list '''
        self.people = []

    def add_entry (self, new_entry):
        ''' Adds a new entry to the list of people in the address book. The new_entry is an instance of the AddressEntry class'''
        self.people.append(new_entry)

    def remove_entry(self, original_index):
        '''Removes an entry from the list using its original index in self.people'''
        try:
            del self.people[original_index]
            return None
        except IndexError:
            return "INVALID ENTRY! Invalid entry index"

    def is_list_empty(self):
        '''Returns True if the address book has no entries, False otherwise'''
        return len(self.people) == 0

    def save(self):
        with open(SAVE_FILE_NAME, 'wb') as file_object:
            pickle.dump(self, file_object)

class AddressEntry (object):
    ''' This class has one instance for each person's details '''
    def __init__ (self, first_name=None, last_name=None, email=None, DOB=None):
        '''Initializes attributs f_name, l_name, email, and birthday. Each arg is a string. Birthday should be a string in the format MM DD, YYYY '''
        self.first_name = first_name
        self.last_name = last_name
        self.email = email
        self.DOB = DOB

    def __repr__ (self):
        '''Given an AddressEntry object self return a readable string representation '''
        return f"AddressEntry(first_name='{self.first_name}', last_name='{self.last_name}', email='{self.email}', DOB='{self.DOB}')"

class Controller(object):
    def __init__(self):
        self.address_book = self.load()
        if self.address_book is None:
            self.address_book = AddressBook()
        self.run_interface()

    def load(self):
        if os.path.exists(SAVE_FILE_NAME):
            with open(SAVE_FILE_NAME, 'rb') as file_object:
                address_book = pickle.load(file_object)
            return address_book
        else:
            return None

    def run_interface(self):
        print(INSTRUCTIONS)
        while True:
            command = input('What would you like to do? (press i to see all the options again) ')
            if command == 'a':
                self.add_entry()
            elif command == 'r':
                self.remove_entry()
            elif command == 'd':
                print("Displaying summaries of all people stored in the address book")
                self.display_summaries()
            elif command == 's':
                self.sort_entries()
            elif command == 'i':
                print(INSTRUCTIONS)
            elif command == 'q':
                user_quit = input(CONFIRM_QUIT_MESSAGE)
                if user_quit.lower() == 'y':
                    print('\nSaving...')
                    self.address_book.save()
                    print("\nThank you for using my address book application; come again soon!")
                    break
                elif user_quit.lower() == 'n':
                    continue
            else:
                print(f"I don't recognize that instruction ({command}) ")

    def add_entry(self):
        print("Adding a new person to the address book")
        print("What is the person's: ")
        first_name = input("First Name? ")
        if first_name == 'q':
            print('Not Adding')
            return
        last_name = input("Last Name? ")
        if last_name == 'q':
            print('Not Adding')
            return
        email = input("Email Address? (if they don't have one, just enter None) ")
        if email == 'q':
            print('Not Adding')
            return
        DOB = input("Date of Birth? (Enter in the format MM DD, YYYY) ")
        if DOB == 'q':
            print('Not Adding')
            return
        new_entry = AddressEntry(first_name, last_name, email, DOB)
        self.address_book.add_entry(new_entry)
        print(f"Added address entry for {first_name} {last_name}\n")

    def display_summaries(self, return_entries=False):
        if self.address_book.is_list_empty():
            print("Cannot display summaries because the address book is empty")
            return [] if return_entries else None
        
        # 存储排序用值、显示用值和原列表索引
        indexed_entries = []
        for idx, entry in enumerate(self.address_book.people):
            sort_values = (entry.first_name, entry.last_name)
            display_values = (entry.first_name, entry.last_name, entry.DOB, entry.email)
            indexed_entries.append( (sort_values, display_values, idx) )
        
        # 按姓名排序
        indexed_entries.sort(key=lambda x: x[0])
        
        # 显示条目
        for display_idx, (_, display_vals, _) in enumerate(indexed_entries, 1):
            entry_str = SUMMARY_TEMPLATE % display_vals
            print(f"{display_idx}: {entry_str}")
        
        # 如果需要返回索引信息(供删除功能使用)
        if return_entries:
            return [ (display_vals, idx) for (_, display_vals, idx) in indexed_entries ]
        return None

    def remove_entry(self):
        print("Removing a person from the address book\n")
        if self.address_book.is_list_empty():
            print("There are no entries to remove from the address book")
            return
        
        # 获取带原索引的排序后条目
        indexed_entries = self.display_summaries(return_entries=True)
        
        while True:
            user_input = input("\nEnter the number of the entry you would like to remove: ")
            if user_input == 'q':
                print("Exiting remove entry")
                return
            
            try:
                display_idx = int(user_input) - 1  # 转成显示列表的0-based索引
                if display_idx < 0 or display_idx >= len(indexed_entries):
                    print(f"INVALID ENTRY! There is no entry number {user_input}")
                    continue
                
                # 获取原列表中的真实索引
                original_idx = indexed_entries[display_idx][1]
                # 删除条目
                result = self.address_book.remove_entry(original_idx)
                
                if result:
                    print(result)
                else:
                    print(f"Entry number {user_input} has been removed")
                    break
            except ValueError:
                print("INVALID ENTRY! Please enter an integer number instead of a character")

    def sort_entries(self):
        print("Sorting Entries")
        if self.address_book.is_list_empty():
            print("Cannot sort because the address book is empty")
            return
        # 复用display_summaries的排序和显示逻辑
        self.display_summaries()

### Main section
controller = Controller()

关键修改说明

  1. 匹配显示与删除的索引:

    • 修改display_summaries方法,让它在显示排序后条目的同时,记录每个条目在原列表中的真实索引。
    • 删除时根据用户输入的显示编号,找到对应条目在原列表中的位置,确保删除的是用户看到的条目。
  2. 修正异常处理:

    • 移除了原删除方法中无效的IndexError捕获,改为在控制器层先验证输入的编号是否合法(是否为整数、是否在条目范围内),错误提示更精准。
  3. 优化空列表判断:

    • 重写is_list_empty方法返回布尔值,让空列表判断逻辑更直观易读。
  4. 修复语法错误:

    • 修正了AddressEntry.__repr__中的多行字符串拼接问题,改用f-string简化代码。

现在测试代码,删除操作会准确对应界面显示的条目,之后的列表展示也会符合你的预期。

内容的提问来源于stack exchange,提问作者user32453215

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:28:51