程序未返回上级菜单直接退出的问题排查
执行以下步骤后,程序直接终止,未重新显示top_menu:
- 选择客户端(输入2);
- 从列表中选择客户端(输入2);
- 按两次回车键。
复现代码
import os from time import sleep from pprint import pprint import json import traceback os.chdir(os.path.dirname(__file__)) active_client = {} flag = True menus = { 'top_menu': ['Create Client', 'Select Client'], 'select_menu':['dynamic'], 'action_menu':['Transactions', 'View Portfolio', 'View Transactions'], 'trans_menu':['Buy', 'Sell', 'Contribution', 'Withdrawal'], 'port_menu': ['Current Portfolio', 'Portfolio History'] } menu_stack = [] # 存储菜单函数引用的栈 def top_menu(): print('enter top') pprint(menu_stack) header ="Equity Portfolio Management System" msg = "Select Task : " tasks = menus['top_menu'] choice = print_menu(header, msg, tasks, False) if choice == 2: print('before') menu_stack.append(top_menu) print(menu_stack) select_client() print('after') pprint(menu_stack) elif choice == "": # 在顶级菜单按回车直接退出 print("Exiting...") with open('clients.json', 'w') as json_file: json.dump(clients, json_file, indent=4) if flag: print(1) return if flag: print(2) pprint(menu_stack) traceback.print_stack() menu_stack.pop()() return def select_client(): global active_client print('appending') pprint(menu_stack) if not clients: print("\nNo clients available. Please create one first.") sleep(2) if flag: print(5) return # 返回上一级菜单 names = [] for index, client in enumerate(clients, 1): names.append(client['first name'] + ' ' + client['last name']) header = 'Client Names' msg = ' Select client : ' choice = print_menu(header, msg, names, False) if choice == "": # 按回车返回顶级菜单 if flag: print(6) pprint(menu_stack) traceback.print_stack() return # 返回上一级菜单 active_client = clients[choice-1] action_menu() if flag: print(7) pprint(menu_stack) traceback.print_stack() return def action_menu(): menu_stack.append(select_client) pprint(menu_stack) header = 'Actions for ' + active_client['first name'] + ' ' + active_client['last name'] msg = 'Select Action : ' tasks = menus['action_menu'] choice = print_menu(header, msg, tasks, False) if choice == "": if flag: print(8) pprint(menu_stack) return # 返回select_client菜单 if flag: print(9) return def print_menu(header, msg, items, header_only): # 获取终端宽度 terminal_width = os.get_terminal_size().columns # 字段尺寸 item_no_size = 10 item_size = 40 # 计算内容居中的起始位置 center_padding = ((terminal_width - item_no_size - item_size) // 2 ) while True: print("\n" + "Babson Enterprises".center(terminal_width)) print(header.center(terminal_width, "*")) print("*" * terminal_width + "\n") if header_only: return print(" " * center_padding + "Task No".ljust(item_no_size) + " Task".ljust(item_size)) print(" " * center_padding + "-" * (item_no_size + item_size) ) # 打印表头下划线 # 打印任务列表 for index, item_name in enumerate(items): print(" " * center_padding + str(index+1).center(item_no_size) + item_name.ljust(item_size)) print("\n") return int_input(" " * center_padding + msg, items) def int_input(msg, items): while True: item_selected = input(msg) if item_selected == '': return '' try: item_selected = int(item_selected) if 1 <= item_selected <= len(items): return item_selected # 输入有效,退出循环 else: msg = "******Invalid choice. Please enter a number " + \ "between 1 and " + str(len(items)) + " : " except ValueError: msg = "Please enter a valid integer." if __name__ == "__main__": with open('clients.json', 'r') as json_file: clients = json.load(json_file) top_menu() print('program end')
问题原因
核心问题在于菜单栈的调用逻辑不统一,导致返回链断裂:
- 从
action_menu按回车返回时,select_client执行完action_menu后直接return,未触发栈的弹出和上一级菜单调用; - 从
select_client按回车返回时,top_menu中执行menu_stack.pop()(),但此时栈状态不符合预期,调用链中断,程序直接退出。
修复方案
统一菜单栈的入栈和返回逻辑,确保每一级菜单的返回都能正确触发上一级菜单的显示:
修改后的关键函数
def top_menu(): print('enter top') pprint(menu_stack) header ="Equity Portfolio Management System" msg = "Select Task : " tasks = menus['top_menu'] choice = print_menu(header, msg, tasks, False) if choice == 2: menu_stack.append(top_menu) select_client() elif choice == "": print("Exiting...") with open('clients.json', 'w') as json_file: json.dump(clients, json_file, indent=4) return # 仅当栈不为空时,弹出并调用上一级菜单 if menu_stack: menu_stack.pop()() def select_client(): global active_client print('appending') pprint(menu_stack) if not clients: print("\nNo clients available. Please create one first.") sleep(2) # 返回上一级菜单 if menu_stack: menu_stack.pop()() return names = [] for index, client in enumerate(clients, 1): names.append(client['first name'] + ' ' + client['last name']) header = 'Client Names' msg = ' Select client : ' choice = print_menu(header, msg, names, False) if choice == "": # 返回top_menu if menu_stack: menu_stack.pop()() return active_client = clients[choice-1] menu_stack.append(select_client) action_menu() def action_menu(): pprint(menu_stack) header = 'Actions for ' + active_client['first name'] + ' ' + active_client['last name'] msg = 'Select Action : ' tasks = menus['action_menu'] choice = print_menu(header, msg, tasks, False) if choice == "": # 返回select_client if menu_stack: menu_stack.pop()() return
修改说明
- 统一入栈时机:进入子菜单前,将当前菜单函数入栈(如
top_menu进入select_client时入栈top_menu,select_client进入action_menu时入栈select_client); - 统一返回逻辑:当用户按回车(空输入)时,直接弹出栈顶并调用上一级菜单,确保返回链完整;
- 移除冗余逻辑:删除原代码中多余的
return后栈操作,避免逻辑冲突; - 栈安全判断:执行
pop()前先检查栈是否为空,防止报错。
内容的提问来源于stack exchange,提问作者DCR
相关产品推荐
相关产品推荐

