如果其他函数会直接给你一个列表索引,那么你不需要将函数名作为字符串处理。相反,直接存储(不调用)列表中的函数:
import file1, file2
functions = [file1.function12, file2.function22, file1.function12]
然后在获得索引后调用它们:
function[index]()
有种方法可以在 Python 中执行所谓的“反射”并从字符串获取匹配命名的函数。但是它们解决的问题比您描述的更高级,而且更困难(尤其是如果您还必须使用模块名称)。
如果您有一个允许从配置文件调用的函数和模块的“白名单”,但仍需要通过字符串查找它们,则可以使用 dict 显式创建映射:
allowed_functions = {
'file1': {
'function11': file1.function11,
'function12': file1.function12
},
'file2': {
'function21': file2.function21,
'function22': file2.function22
}
}
然后调用函数:
try:
func = allowed_functions[module_name][function_name]
except KeyError:
raise ValueError("this function/module name is not allowed")
else:
func()
最高级的方法是如果您需要从作者创建的“插件”模块加载代码。您可以使用标准库importlib 包,使用字符串名称查找要作为模块导入的文件,并动态导入。它看起来像:
from importlib.util import spec_from_file_location, module_from_spec
# Look for the file at the specified path, figure out the module name
# from the base file name, import it and make a module object.
def load_module(path):
folder, filename = os.path.split(path)
basename, extension = os.path.splitext(filename)
spec = spec_from_file_location(basename, path)
module = module_from_spec(spec)
spec.loader.exec_module(module)
assert module.__name__ == basename
return module
这仍然不安全,因为它可以在文件系统的任何位置查找模块。最好自己指定文件夹,并且只允许在配置文件中使用文件名;但是您仍然必须通过在“文件名”中使用“..”和“/”之类的内容来防止黑客入侵路径。
(我有一个项目可以做这样的事情。它从同样受用户控制的白名单中选择路径,所以我必须警告我的用户不要相互信任路径白名单文件。我也在目录中搜索模块,然后仅根据目录中的插件创建可能使用的插件白名单 - 所以没有带有“..”的有趣游戏。我仍然担心我忘记了什么。)
一旦你有了一个模块名,你就可以通过名字来获取一个函数,比如:
dynamic_module = load_module(some_path)
try:
func = getattr(dynamic_module, function_name)
except AttributeError:
raise ValueError("function not in module")
无论如何,没有理由eval 任何东西,或者根据用户输入生成和导入代码。这是最不安全的。