Программирование

Как вызвать функцию модуля по имени в Python (getattr)

Динамический вызов функции модуля по строковому имени в Python с помощью getattr и importlib. Примеры кода, проверки безопасности, обработка async-функций и полный шаблон реализации call(module, func_name). Избегайте eval для безопасности.

Как динамически вызвать функцию модуля, используя её имя в виде строки в Python? Например, если у меня есть:

python
import foo
func_name = "bar"
call(foo, func_name) # Это должно вызвать foo.bar()

Как правильно реализовать эту функциональность?

Чтобы динамически вызвать python функцию модуля по её строковому имени, используйте встроенную функцию getattr: func = getattr(foo, func_name) и затем func(*args, **kwargs). Для динамического импорта модуля применяйте importlib.import_module, избегайте eval и всегда проверяйте, что найденный атрибут — callable, обрабатывая AttributeError/TypeError; для coroutine-функций учитывайте inspect.iscoroutinefunction и вызывайте их через await или asyncio.run.


Содержание


Как вызвать функцию модуля по имени (getattr)

Самый простой и обычно правильный способ — использовать getattr. Это стандартный механизм для динамического доступа к атрибутам (включая функции) модуля:

python
# пример простой обёртки
def call(module, func_name, *args, **kwargs):
 func = getattr(module, func_name) # если нет — AttributeError
 if not callable(func):
 raise TypeError(f"{func_name!r} is not callable")
 return func(*args, **kwargs)

# использование
import foo
result = call(foo, "bar", 1, 2) # эквивалент foo.bar(1, 2)

Если хотите избежать исключения при отсутствии атрибута, используйте значение по умолчанию у getattr и проверку:

python
func = getattr(module, func_name, None)
if func is None or not callable(func):
 raise AttributeError(f"Функция {func_name!r} не найдена или не является вызываемой")
return func(*args, **kwargs)

Подробности по getattr в официальной документации: https://docs.python.org/3/library/functions.html#getattr


Динамический импорт модуля и путь вида “package.module.func”

Если у вас исходно есть строка с именем модуля (например, "foo" или путь "pkg.sub.foo"), используйте importlib.import_module для надёжного импорта:

python
import importlib

def call_from_module_name(module_name, func_name, *args, **kwargs):
 module = importlib.import_module(module_name)
 func = getattr(module, func_name, None)
 if not callable(func):
 raise AttributeError(...)
 return func(*args, **kwargs)

Если передаётся полная строка вида "foo.bar" (модуль + функция), можно разобрать так:

python
def call_from_path(path, *args, **kwargs):
 module_name, func_name = path.rsplit(".", 1)
 module = importlib.import_module(module_name)
 return call(module, func_name, *args, **kwargs)

importlib.import_module предпочтительнее __import__ — это обсуждается в руководствах и примерах по динамическому импорту и вызову функций (см. пример и объяснения на GeeksforGeeks): https://www.geeksforgeeks.org/python-call-a-function-by-a-string-name-python/


Проверки и безопасность при динамическом вызове функции в Python

Что делать, если имя функции приходит от пользователя или из внешнего источника? Нельзя просто вызывать всё подряд — это риск.

Рекомендации:

  • Не используйте eval. Это небезопасно для любых данных из внешних источников.
  • Ограничьте список разрешённых функций (whitelist) или явно передавайте маппинг допустимых функций.
  • Проверяйте, что атрибут существует и что он callable.
  • При необходимости проверяйте сигнатуру через inspect.signature перед вызовом.

Пример whitelist-подхода:

python
# явный список разрешённых функций
ALLOWED = {
 "bar": foo.bar,
 "baz": foo.baz,
}

def safe_call(func_name, *args, **kwargs):
 func = ALLOWED.get(func_name)
 if func is None:
 raise ValueError("Недопустимое имя функции")
 return func(*args, **kwargs)

Проверка аргументов:

python
import inspect

sig = inspect.signature(func)
try:
 sig.bind(*args, **kwargs) # проверка соответствия аргументов
except TypeError as e:
 raise TypeError("Неправильные аргументы для функции") from e

Ещё одна тонкость: hasattr(obj, name) внутренно вызывает getattr и может “поглотить” AttributeError, возникающий внутри свойства — учитывайте это при валидации. Практические обсуждения и примеры использования getattr vs eval можно посмотреть здесь: https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string


Асинхронные функции и методы класса

А что если вызываемая функция — coroutine (async def)? Или это метод класса/экземпляра?

  1. Coroutine:
  • В синхронном коде можно запускать coroutine через asyncio.run.
  • Внутри async-контекста — await.

Примеры:

python
import inspect, asyncio

def call_sync(module, func_name, *args, **kwargs):
 func = getattr(module, func_name)
 if inspect.iscoroutinefunction(func):
 return asyncio.run(func(*args, **kwargs)) # синхронный запуск coroutine
 return func(*args, **kwargs)

async def call_async(module, func_name, *args, **kwargs):
 func = getattr(module, func_name)
 result = func(*args, **kwargs)
 if inspect.isawaitable(result):
 return await result
 return result
  1. Методы класса/экземпляра:
  • Если у вас экземпляр obj, то getattr(obj, "method")() вернёт уже привязанный метод — self не нужно передавать.
  • Если берёте метод с класса getattr(C, "method"), то его нужно вызвать передав экземпляр явно: getattr(C, "method")(obj) — это редкий кейс, но важно понимать разницу.

Полный пример: надёжная реализация call(module, func_name)

Ниже — компактный, практичный и достаточно безопасный шаблон, который покрывает динамический импорт, проверку и async-поддержку в простом варианте.

python
import importlib
import inspect
import asyncio
from types import ModuleType
from typing import Any, Union

def _import_module(module_or_name: Union[ModuleType, str]) -> ModuleType:
 if isinstance(module_or_name, str):
 return importlib.import_module(module_or_name)
 return module_or_name

def call(module_or_name: Union[ModuleType, str], func_name: str, *args, **kwargs) -> Any:
 module = _import_module(module_or_name)
 func = getattr(module, func_name, None)
 if func is None or not callable(func):
 raise AttributeError(f"Модуль {getattr(module, '__name__', str(module))!s} не содержит вызываемой {func_name!r}")

 # Необязательная проверка аргументов (можно убрать для производительности)
 try:
 inspect.signature(func).bind_partial(*args, **kwargs)
 except TypeError:
 # сигнатура не совместима — дадим ошибку на этапе вызова
 pass

 if inspect.iscoroutinefunction(func):
 # Запуск coroutine в синхронном коде
 return asyncio.run(func(*args, **kwargs))
 return func(*args, **kwargs)

# Примеры использования:
# import foo
# call(foo, "bar", 1, 2)
# call("foo", "bar", 1, 2) # строка с именем модуля — тоже работает

Если вы работаете внутри async-функции, используйте call_async-версию, которая await-ит результат вместо asyncio.run.


Источники


Заключение

Коротко: для динамического вызова функции модуля по имени используйте getattr вместе с importlib.import_module при необходимости динамического импорта. Это стандартный, понятный и относительно безопасный способ вызова функции в Python — при условии валидации входных данных, проверки callable и аккуратной работы с async-функциями.

Авторы
Проверено модерацией
Модерация
Как вызвать функцию модуля по имени в Python (getattr)