Если другая функция выдаст вам индекс списка напрямую, вам не нужно иметь дело с именами функций в виде строк. Вместо этого непосредственно сохраняйте (без вызова) функции в списке:
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()
Наиболее продвинутый подход заключается в том, если вам нужно загрузить код из модуля «плагина», созданного автором. Вы можете использовать модуль import
, чтобы использовать имя строки, чтобы найти файл для импорта в качестве модуля и импортировать его динамически. Это выглядит примерно так:
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
чего-либо, или для генерации и импорта кода на основе пользовательского ввода. Это наиболее небезопасно из всех.