<p>如果另一个函数将直接给您一个列表索引,那么您不需要将函数名作为字符串处理。相反,直接存储(不调用)列表中的函数:</p>
<pre><code>import file1, file2
functions = [file1.function12, file2.function22, file1.function12]
</code></pre>
<p>一旦找到索引,就给他们打电话:</p>
<pre><code>function[index]()
</code></pre>
<p>有<em>种</em>方法可以在Python中执行所谓的“反射”,并从字符串获取匹配的命名函数。但它们解决的问题比您所描述的更高级,而且更困难(特别是如果您还必须使用模块名称)。你知道吗</p>
<hr/>
<p>如果您有一个允许从配置文件调用的函数和模块的“白名单”,但仍然需要按字符串查找它们,则可以使用dict显式创建映射:</p>
<pre><code>allowed_functions = {
'file1': {
'function11': file1.function11,
'function12': file1.function12
},
'file2': {
'function21': file2.function21,
'function22': file2.function22
}
}
</code></pre>
<p>然后调用函数:</p>
<pre><code>try:
func = allowed_functions[module_name][function_name]
except KeyError:
raise ValueError("this function/module name is not allowed")
else:
func()
</code></pre>
<hr/>
<p>最先进的方法是,如果您需要从作者创建的“插件”模块加载代码。您可以使用<code>import</code>模块使用字符串名称来查找要作为模块导入的文件,并动态导入它。它看起来像:</p>
<pre><code>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
</code></pre>
<p>这仍然是不安全的,因为它可以在文件系统的任何地方查找模块。如果您自己指定文件夹,并且只允许在配置文件中使用一个文件名,那就更好了;但是您仍然必须通过在“文件名”中使用“.”和“/”之类的内容来防止黑客攻击路径。你知道吗</p>
<p>(我有一个这样的项目。它从用户控制的白名单中选择路径,因此我必须警告我的用户不要相互信任路径白名单文件。我也会在目录中搜索模块,然后列出可能会用到的插件的白名单,只基于目录中的插件-所以没有带“.”的有趣游戏。我仍然担心我忘了什么。)</p>
<p>一旦你有了一个模块名,你就可以从中得到一个函数名,比如:</p>
<pre><code>dynamic_module = load_module(some_path)
try:
func = getattr(dynamic_module, function_name)
except AttributeError:
raise ValueError("function not in module")
</code></pre>
<p>无论如何,没有理由<code>eval</code>任何东西,或者根据用户输入生成和导入代码。这是最不安全的。你知道吗</p>