Python中如何获取exec()定义的可调用函数?求通用正确方法
问题描述
我想要实现一个exec_myfunc函数,能够执行任意用户以字符串形式定义的函数,并传入输入值10。用户定义的函数示例如下:
func1_str = """ def myfunc1(x): return x """ func2_str = """ def myfunc2(x): return x**2 """
我目前用了一种不太规范的方法:通过正则表达式提取def和(之间的函数名,实现代码如下:
def exec_myfunc(func_str: str): import re exec(func_str) myfunc_str = re.search(r'def(.*)\(', func_str).group(1).strip() return eval(myfunc_str)(10) print(exec_myfunc(func1_str)) # 10 print(exec_myfunc(func2_str)) # 100
请问实现该需求的通用且正确的方法是什么?
通用且正确的实现方法
方法1:使用自定义命名空间执行exec
直接利用exec的命名空间参数捕获定义的函数,既不会污染全局命名空间,也无需依赖正则匹配:
def exec_myfunc(func_str: str): namespace = {} exec(func_str, namespace) # 假设输入字符串只定义了一个函数,提取唯一的可调用对象 func = next(v for v in namespace.values() if callable(v)) return func(10) # 测试示例 func1_str = """ def myfunc1(x): return x """ func2_str = """ def myfunc2(x): return x**2 """ print(exec_myfunc(func1_str)) # 输出10 print(exec_myfunc(func2_str)) # 输出100
这种方法的优势:
- 隔离执行环境,避免污染全局变量,安全性更高
- 无需依赖字符串格式,能兼容各种合法的函数定义写法
- 直接获取函数对象,逻辑清晰可靠
如果需要支持多函数定义但指定执行目标,可以约定固定函数名(比如myfunc),直接从命名空间中取值:
def exec_myfunc(func_str: str): namespace = {} exec(func_str, namespace) return namespace['myfunc'](10) # 用户需按约定定义名为myfunc的函数 func_str = """ def myfunc(x): return x*3 """ print(exec_myfunc(func_str)) # 输出30
方法2:用AST模块解析(严谨性拉满)
如果需要严格校验输入代码的合法性,或者处理更复杂的函数定义场景,使用Python内置的ast模块解析代码语法树是最优解:
import ast def exec_myfunc(func_str: str): tree = ast.parse(func_str) # 遍历语法树,筛选出所有函数定义节点 funcs = [node for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)] if not funcs: raise ValueError("输入字符串中未定义任何函数") # 取第一个函数定义,编译并执行 func_def = funcs[0] code = compile(ast.Module(body=[func_def], type_ignores=[]), filename="<ast>", mode="exec") namespace = {} exec(code, namespace) return namespace[func_def.name](10) # 测试 print(exec_myfunc(func1_str)) # 输出10 print(exec_myfunc(func2_str)) # 输出100
该方法的核心优势:
- 能精准识别合法的函数定义,自动过滤无效代码或语法错误
- 完全规避正则匹配的各种边缘场景(比如带注释、装饰器的函数)
- 可扩展性强,能进一步处理函数参数校验、装饰器解析等需求
原方法的问题
原正则提取的方式存在诸多缺陷:
- 无法处理带括号注释的函数定义(比如
def func(x): # 这是(测试)),会匹配到错误内容 - 对函数定义的格式依赖极强,稍有变化(比如多行定义、带装饰器)就会失效
- 使用全局
exec和eval会污染全局命名空间,还存在潜在的安全风险
内容的提问来源于stack exchange,提问作者Shaun Han
相关产品推荐
相关产品推荐

