Ответ 1
Ваш код почти прав.
См. __import__
.
def doSomething(name):
name = "package." + name
mod = __import__(name, fromlist=[''])
mod.doSomething()
В Python, как вы динамически добавляете модули в пакет во время работы вашей программы.
Я хочу иметь возможность добавлять модули в каталог пакетов из внешнего процесса и использовать эти новые модули в моей программе:
import package
def doSomething(name):
pkg = __import__("package." + name)
mod = getattr(pkg, name)
mod.doSomething()
Как мне это сделать?
Ваш код почти прав.
См. __import__
.
def doSomething(name):
name = "package." + name
mod = __import__(name, fromlist=[''])
mod.doSomething()
Бастиен уже ответил на вопрос, так или иначе вы можете найти полезную эту функцию, которую я использую для загрузки всех модулей из подпапки в словаре:
def loadModules():
res = {}
import os
# check subfolders
lst = os.listdir("services")
dir = []
for d in lst:
s = os.path.abspath("services") + os.sep + d
if os.path.isdir(s) and os.path.exists(s + os.sep + "__init__.py"):
dir.append(d)
# load the modules
for d in dir:
res[d] = __import__("services." + d, fromlist = ["*"])
return res
Это другое - создать экземпляр объекта классом, определенным в одном из модулей, загруженных первой функцией:
def getClassByName(module, className):
if not module:
if className.startswith("services."):
className = className.split("services.")[1]
l = className.split(".")
m = __services__[l[0]]
return getClassByName(m, ".".join(l[1:]))
elif "." in className:
l = className.split(".")
m = getattr(module, l[0])
return getClassByName(m, ".".join(l[1:]))
else:
return getattr(module, className)
Простым способом использования этих функций является следующее:
mods = loadModules()
cls = getClassByName(mods["MyModule"], "submodule.filepy.Class")
obj = cls()
Очевидно, вы можете заменить все ссылки в подпапках "services" параметрами.
import importlib
module = importlib.import_module('my_package.my_module')
my_class = getattr(module, 'MyClass')
my_instance = my_class()
Один трюк с ответом Бастиена... Функция __import__()
возвращает объект пакета, а не объект модуля. Если вы используете следующую функцию, она будет динамически загружать модуль из пакета и возвращать вам модуль, а не пакет.
def my_import(name):
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
Затем вы можете сделать:
mod = my_import('package.' + name)
mod.doSomething()
Чтобы обнаружить изменения в каталоге, в Linux вы можете использовать pyinotify (здесь - хороший рабочий пример); на Mac, fsevents (через пакет PyObjC, который поставляется вместе с вашим Mac); в Windows, Уведомления об изменении каталога через win32api (или модуль стандартной библиотеки Python ctypes
). AFAIK, никто не завернул эти различные подходы в один переносной пакет. (Конечно, в худшем случае вы можете вернуться к "низкотехнологичным" подходам, таким как периодический опрос, как Тим Золотая статья, возможно, с касание "сигналов от внешнего процесса" через сигнал и т.д.).
После того, как у вас есть уведомление и имя нового или измененного модуля, код, отображаемый в вопросе, должен работать.
Добавьте каталог модуля в sys.path
и используйте нормальный оператор import
.