如何动态调用Python模块中任意文件的函数并构建可扩展架构
动态数学问题求解库实现方案
一、核心实现思路
- 用
os模块遍历solves目录,自动识别prob\d+\.py格式的问题文件 - 用
importlib动态导入模块,彻底避免手动写import语句 - 用
inspect模块提取每个模块中solve_开头的求解方法 - 自动生成交互式菜单,用户选完问题和方法后,按规则收集参数并执行
二、代码实现
1. 问题文件示例(solves/prob002.py)
每个问题文件需遵循简单规范,方便主程序识别:
# solves/prob002.py # 可选:定义问题描述,用于菜单展示 PROBLEM_DESC = "求解两数之和的平方:(a + b)²" # 可选:定义参数规则,用于自动收集用户输入 PARAMS = [ {"name": "a", "type": float, "desc": "第一个数"}, {"name": "b", "type": float, "desc": "第二个数"} ] def solve_1(a, b): """方法1:展开公式直接计算""" return a**2 + 2*a*b + b**2 def solve_2(a, b): """方法2:先求和再平方""" return (a + b)**2
2. 主程序代码(main.py)
这部分是核心,负责动态加载、菜单生成和执行逻辑:
import os import importlib import inspect def load_problems(): """自动加载solves目录下所有符合规则的问题模块""" problems = {} solves_dir = os.path.join(os.path.dirname(__file__), "solves") # 遍历目录找问题文件 for filename in os.listdir(solves_dir): if filename.startswith("prob") and filename.endswith(".py"): module_name = filename[:-3] # 去掉.py后缀 # 动态导入模块 module = importlib.import_module(f"solves.{module_name}") # 提取所有solve_开头的方法 solve_methods = {} for name, func in inspect.getmembers(module, inspect.isfunction): if name.startswith("solve_"): solve_methods[name] = func # 提取问题描述和参数规则(没有就用默认值) problem_desc = getattr(module, "PROBLEM_DESC", f"问题{module_name}") params = getattr(module, "PARAMS", []) problems[module_name] = { "desc": problem_desc, "params": params, "solves": solve_methods } return problems def get_user_input(params): """根据参数规则收集用户输入,自动做类型校验""" inputs = {} for param in params: while True: try: value = input(f"请输入{param['desc']}({param['name']}): ") inputs[param['name']] = param['type'](value) break except ValueError: print(f"输入错误,请输入{param['type'].__name__}类型的值") return inputs def main(): problems = load_problems() if not problems: print("未找到任何问题文件,请检查solves目录") return # 显示问题选择菜单 print("=== 数学问题求解库 ===") problem_list = list(problems.items()) for idx, (key, info) in enumerate(problem_list, 1): print(f"{idx}. {info['desc']}") # 处理用户选择问题 while True: try: problem_idx = int(input("请选择问题编号:")) - 1 if 0 <= problem_idx < len(problem_list): break print("编号无效,请重新输入") except ValueError: print("请输入有效数字") selected_key, selected_info = problem_list[problem_idx] solve_methods = selected_info["solves"] if not solve_methods: print("该问题暂无求解方法") return # 显示求解方法选择菜单 print("\n=== 选择求解方法 ===") solve_list = list(solve_methods.items()) for idx, (name, func) in enumerate(solve_list, 1): # 显示方法的文档说明(如果有的话) doc = func.__doc__.strip() if func.__doc__ else "无描述" print(f"{idx}. {name}: {doc}") # 处理用户选择求解方法 while True: try: solve_idx = int(input("请选择求解方法编号:")) - 1 if 0 <= solve_idx < len(solve_list): break print("编号无效,请重新输入") except ValueError: print("请输入有效数字") # 收集参数并执行 selected_func = solve_list[solve_idx][1] inputs = get_user_input(selected_info["params"]) result = selected_func(**inputs) print(f"\n求解结果:{result}") if __name__ == "__main__": main()
三、灵活性与可扩展性说明
- 自动适配新问题:新增
probXX.py到solves目录后,主程序会自动识别加载,完全不用修改main.py - 低侵入规范:新问题文件只要满足以下几点就能兼容:
- 文件名以
prob开头+数字结尾(如prob003.py) - 求解方法以
solve_开头(命名可自定义,比如solve_optimized) - 可选定义
PROBLEM_DESC和PARAMS来优化菜单展示和输入校验
- 文件名以
- 方法描述自动提取:求解方法的文档字符串会自动显示在菜单里,不用额外配置
- 参数校验自动化:通过
PARAMS定义的类型,主程序会自动校验用户输入,避免类型错误
内容的提问来源于stack exchange,提问作者steenbergh
相关产品推荐
相关产品推荐

