如何从Python包中提取指定脚本实际调用的函数?
不修改原包前提下提取脚本依赖的包内函数
完全可以做到,分识别依赖函数和提取为独立文件两步操作:
一、识别脚本调用的mypackage内函数
1. 静态分析(无需运行脚本)
用Python内置的ast模块解析脚本的抽象语法树,追踪所有来自mypackage的函数调用:
import ast import importlib def get_mypackage_deps(script_path): used_funcs = set() with open(script_path, 'r', encoding='utf-8') as f: tree = ast.parse(f.read()) # 遍历所有调用节点 for node in ast.walk(tree): # 处理形如`mypackage.mymodule.func1()`的调用 if isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute): try: # 解析模块对象 mod_path = [] current = node.func.value while isinstance(current, ast.Attribute): mod_path.append(current.attr) current = current.value if isinstance(current, ast.Name): mod_path.append(current.id) mod_path.reverse() mod_fullname = '.'.join(mod_path) if mod_fullname.startswith('mypackage'): func_name = node.func.attr used_funcs.add((mod_fullname, func_name)) except: continue # 处理形如`from mypackage.mymodule import func1; func1()`的调用 elif isinstance(node, ast.Call) and isinstance(node.func, ast.Name): func_name = node.func.id # 检查全局变量中的函数是否来自mypackage if func_name in globals(): func_obj = globals()[func_name] if hasattr(func_obj, '__module__') and func_obj.__module__.startswith('mypackage'): used_funcs.add((func_obj.__module__, func_name)) return used_funcs
2. 动态追踪(运行脚本,准确率更高)
用sys.settrace跟踪脚本运行时的所有函数调用,过滤出mypackage内的函数:
import sys import importlib traced_funcs = set() def trace_handler(frame, event, arg): if event != 'call': return trace_handler # 获取当前调用函数的模块名 mod_name = frame.f_globals.get('__name__', '') if mod_name.startswith('mypackage'): func_name = frame.f_code.co_name traced_funcs.add((mod_name, func_name)) return trace_handler # 启动追踪并运行脚本 sys.settrace(trace_handler) importlib.import_module('your_script_name') # 替换为你的脚本名(不含.py) sys.settrace(None)
二、提取函数为独立文件
用inspect模块获取函数源代码,写入新文件,同时处理必要的依赖:
import inspect import importlib def extract_funcs(func_list, output_file): with open(output_file, 'w', encoding='utf-8') as f: # 先写入外部依赖的导入语句(比如numpy) f.write('import numpy as np\n\n') for mod_fullname, func_name in func_list: # 导入目标模块 module = importlib.import_module(mod_fullname) func = getattr(module, func_name) # 获取函数源代码 try: source_code = inspect.getsource(func) f.write(f'# 来自 {mod_fullname}\n{source_code}\n\n') except OSError: print(f"无法获取函数 {func_name} 的源代码") # 使用前面识别出的函数列表 extract_funcs(traced_funcs, 'extracted_mypackage_funcs.py')
注意事项
- 静态分析无法识别动态调用(比如
getattr(mypackage.mymodule, 'func1')()),这种场景优先用动态追踪。 - 如果提取的函数依赖
mypackage内的其他函数、常量或类,需要递归识别并提取,否则独立文件会因缺失依赖报错。 - 类方法的提取需要特殊处理,通常需要提取整个类而非单个方法。
内容的提问来源于stack exchange,提问作者DiveIntoML
相关产品推荐
相关产品推荐

