Telegram提醒机器人字典取值异常:传入month触发week的KeyError
问题:为何type为'month'的Record会触发KeyError: '25'?
我正在开发一款Telegram提醒机器人,帮助用户管理各类待办提醒,支持每日、每周、每月等多种重复类型。机器人提供/reminders命令用于展示用户的提醒记录,为了生成对应的回复文本,我编写了make_record_text函数,通过一个periods字典根据提醒类型匹配对应的文本格式。但遇到一个问题:当传入type为'month'的Record对象时,函数总是触发KeyError: '25',该错误来自week类型对应的days_of_week_dict[record.time]操作,而我本应获取month对应的字典值。
相关代码
主命令函数
@dp.message_handler(commands=['reminders'], state="*") async def show_reminder(message: types.Message): """ Shows user's records """ records = get_records(int(message.from_user.id)) if records: await message.answer(messages.show_records_message) for item in records: await message.answer(make_record_text(item)) else: ###
生成文本的函数
def make_record_text(record: Record) -> str: """ Creates an answer with user's record using Record """ days_of_week_dict = { 'monday': "every monday", 'tuesday': "every tuesday", 'wednesday': "every wednesday", 'thursday': "every thursday", 'friday': "every friday", 'saturday': "every saturday", 'sunday': "every sunday", } print(f'record = {record}') print(f'record type = {record.type}') print(f'record date = {record.date}') print(f'record time = {record.time}') periods = {'year': f'every year {record.time}', 'once': record.date, 'week': f'{days_of_week_dict[record.time]}', 'month': f'every month on {record.time}', 'several_min': f'every {record.time} minutes', 'everyday': f'everyday at {record.time}', 'several_hours': f'every {record.time} hours', 'every_few_days': f'every {record.time} days', } return messages.list_records_message.format(title=record.title, date=periods[record.type], id=record.id)
Record类定义
class Record(NamedTuple): user: int title: str date: Union[type(datetime.datetime), str] time: Union[type(datetime.datetime), str] type: Literal['everyday', 'few_times_a_day', 'every_few_days', 'week', 'month', 'year', 'once'] need_delete: bool id: Optional[int]
输出日志
record = Record(user=1234, title='month', date='month', time='25', type='month', need_delete=0, id=1) record type = month record date = month record time = 25
错误信息
File "C:\Users\pizhlo21\Desktop\Folder\python\tg_bot_reminder\controller.py", line 121, in show_reminder await message.answer(make_record_text(item)) File "C:\Users\pizhlo21\Desktop\Folder\python\tg_bot_reminder\controller.py", line 146, in make_record_text 'week': f'{days_of_week_dict[record.time]}', KeyError: '25'
解答
原因很直接:Python在创建字典时,会立即计算所有键对应的值,不管你后续会不会用到这个键。
你写的periods字典里,每个键的值都是提前计算好的——哪怕当前处理的是type为'month'的record,Python还是会执行f'{days_of_week_dict[record.time]}'这行代码来生成'week'键对应的值。而此时record.time是'25',这个值不在days_of_week_dict的键列表里,所以直接触发了KeyError。
解决办法
方法1:改用条件判断生成对应文本
这种方式最直观,只在需要的时候计算对应类型的文本,避免提前执行所有分支:
def make_record_text(record: Record) -> str: days_of_week_dict = { 'monday': "every monday", 'tuesday': "every tuesday", 'wednesday': "every wednesday", 'thursday': "every thursday", 'friday': "every friday", 'saturday': "every saturday", 'sunday': "every sunday", } # 根据提醒类型生成对应的日期文本 if record.type == 'year': date_text = f'every year {record.time}' elif record.type == 'once': date_text = record.date elif record.type == 'week': date_text = days_of_week_dict[record.time] elif record.type == 'month': date_text = f'every month on {record.time}' elif record.type == 'several_min': date_text = f'every {record.time} minutes' elif record.type == 'everyday': date_text = f'everyday at {record.time}' elif record.type == 'several_hours': date_text = f'every {record.time} hours' elif record.type == 'every_few_days': date_text = f'every {record.time} days' else: date_text = '未知提醒类型' # 处理未定义的类型,避免报错 return messages.list_records_message.format(title=record.title, date=date_text, id=record.id)
方法2:用lambda延迟计算字典值
把字典里的值改成lambda函数,这样只有当你调用对应的lambda时才会计算值,避免提前执行所有分支:
def make_record_text(record: Record) -> str: days_of_week_dict = { 'monday': "every monday", 'tuesday': "every tuesday", 'wednesday': "every wednesday", 'thursday': "every thursday", 'friday': "every friday", 'saturday': "every saturday", 'sunday': "every sunday", } periods = { 'year': lambda: f'every year {record.time}', 'once': lambda: record.date, 'week': lambda: days_of_week_dict[record.time], 'month': lambda: f'every month on {record.time}', 'several_min': lambda: f'every {record.time} minutes', 'everyday': lambda: f'everyday at {record.time}', 'several_hours': lambda: f'every {record.time} hours', 'every_few_days': lambda: f'every {record.time} days', } # 调用对应lambda计算文本 date_text = periods[record.type]() return messages.list_records_message.format(title=record.title, date=date_text, id=record.id)
内容的提问来源于stack exchange,提问作者pizhlo
相关产品推荐
相关产品推荐

