You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python跨模块按字符串调用函数:替代eval()的更优方案咨询

Python跨模块按字符串调用函数:替代eval()的更优方案咨询

嗨,这个场景我刚学Python的时候也踩过坑!跨模块按字符串调用函数确实容易遇到命名空间的问题,你用eval()虽然能跑,但确实有安全隐患——如果choice参数是外部传入的(比如用户输入),恶意输入可能会执行任意代码,风险很大。下面给你几个更Pythonic、更安全的替代方案:

方案一:传递函数所在的命名空间

核心思路是让function_caller获取到调用者的全局命名空间,而不是局限于tools.py自己的命名空间。可以用inspect模块获取调用者的上下文,或者让调用者显式传入命名空间:

修改后的tools.py:

import inspect

def function_caller(choice, namespace=None):
    # 如果没传命名空间,自动获取调用者的全局命名空间
    if namespace is None:
        caller_frame = inspect.currentframe().f_back
        namespace = caller_frame.f_globals
    
    chosen_func = namespace.get(choice)
    if not callable(chosen_func):
        raise ValueError(f"函数 {choice} 不存在或不可调用")
    
    return chosen_func()

project.py调用示例:

import tools

def function1():
    # 你的业务逻辑
    return "function1的返回结果"

which_one = "function1"
# 不用额外传参数,自动获取project.py的全局命名空间
the_result = tools.function_caller(which_one)
# 或者显式传入globals(),更清晰
# the_result = tools.function_caller(which_one, globals())

这个方案的好处是灵活,不用硬编码模块名,而且比eval安全得多——只会从指定命名空间里查找函数,不会执行任意代码。

方案二:显式传递函数映射字典(最推荐)

这是最清晰、最安全的做法,完全避免命名空间的依赖,让调用者明确指定哪些函数可以被调用:

修改后的tools.py:

def function_caller(choice, func_map):
    chosen_func = func_map.get(choice)
    if not callable(chosen_func):
        raise ValueError(f"函数 {choice} 不在可用列表中或不可调用")
    
    return chosen_func()

project.py调用示例:

import tools

def function1():
    return "function1的返回结果"

def function2():
    return "function2的返回结果"

# 定义可用函数的映射字典
available_functions = {
    "function1": function1,
    "function2": function2
}

which_one = "function1"
the_result = tools.function_caller(which_one, available_functions)

如果觉得每次传字典麻烦,还可以给tools.py加一个函数注册机制,类似很多框架的做法:

带注册器的tools.py:

# 存储已注册的函数
_registered_functions = {}

def register_function(func_name):
    """装饰器:注册函数到可用列表"""
    def decorator(func):
        _registered_functions[func_name] = func
        return func
    return decorator

def function_caller(choice):
    chosen_func = _registered_functions.get(choice)
    if not callable(chosen_func):
        raise ValueError(f"未注册函数 {choice}")
    
    return chosen_func()

project.py调用示例:

from tools import register_function, function_caller

@register_function("function1")
def function1():
    return "function1的返回结果"

which_one = "function1"
the_result = function_caller(which_one)

这个方案的优势在于:完全可控,你明确知道哪些函数能被调用,不会出现意外;代码可读性也更高,别人一看就知道哪些函数是给function_caller用的。

方案三:通过模块导入获取函数(不推荐,仅作参考)

如果你的函数确实都在主模块(__main__),可以用importlib安全地导入模块并获取函数,比eval安全:

修改后的tools.py:

import importlib

def function_caller(choice):
    # 导入主模块
    main_module = importlib.import_module("__main__")
    # 获取函数
    chosen_func = getattr(main_module, choice, None)
    if not callable(chosen_func):
        raise ValueError(f"函数 {choice} 不存在或不可调用")
    
    return chosen_func()

但这个方案局限性很大——如果后续你的函数移到其他子模块,代码就失效了,灵活性差,所以不推荐作为长期方案。

总结

  • 优先选方案二(显式映射/注册):最安全、最清晰,符合Python的"显式优于隐式"原则
  • 如果不想维护映射字典,可选方案一:灵活且安全
  • 绝对要避免eval/exec:尤其是当choice参数可能来自外部输入时,会有严重的代码注入风险

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 06:54:50