Вопрос-Ответ

How can I import a module dynamically given its name as string?

Как я могу импортировать модуль, которому динамически присваивается его имя в виде строки?

Я пишу приложение на Python, которое принимает команду в качестве аргумента, например:

$ python myapp.py command1

Я хочу, чтобы приложение было расширяемым, то есть могло добавлять новые модули, реализующие новые команды, без необходимости изменять основной исходный код приложения. Дерево выглядит примерно так:

myapp/
__init__.py
commands/
__init__.py
command1.py
command2.py
foo.py
bar.py

Итак, я хочу, чтобы приложение находило доступные командные модули во время выполнения и выполняло соответствующий.

Python определяет __import__() функцию, которая принимает строку в качестве имени модуля:


__import__(name, globals=None, locals=None, fromlist=(), level=0)


Функция импортирует модуль name, потенциально используя заданный globals и locals, чтобы определить, как интерпретировать имя в контексте пакета. В fromlist задаются имена объектов или подмодулей, которые должны быть импортированы из модуля, указанного в name.


Источник: https://docs.python.org/3/library/functions.html#__import __


Итак, в настоящее время у меня есть что-то вроде:

command = sys.argv[1]
try:
command_module = __import__("myapp.commands.%s" % command, fromlist=["myapp.commands"])
except ImportError:
# Display error message

command_module.run()

Это работает просто отлично, мне просто интересно, возможно ли более идиоматичный способ выполнить то, что мы делаем с этим кодом.

Обратите внимание, что я специально не хочу использовать eggs или точки расширения. Это не проект с открытым исходным кодом, и я не ожидаю, что там будут "плагины". Суть в том, чтобы упростить основной код приложения и устранить необходимость изменять его каждый раз, когда добавляется новый командный модуль.


Смотрите также: Как мне импортировать модуль с указанием полного пути?

Переведено автоматически
Ответ 1

С Python старше 2.7 / 3.1 вы делаете это примерно так.

Для получения более новых версий см. importlib.import_module для Python 2 и Python 3.

Или с помощью __import__ вы можете импортировать список модулей, выполнив это:

>>> moduleNames = ['sys', 'os', 're', 'unittest'] 
>>> moduleNames
['sys', 'os', 're', 'unittest']
>>> modules = map(__import__, moduleNames)

Вырвано прямо из погружения в Python.

Ответ 2

The recommended way for Python 2.7 and 3.1 and later is to use importlib module:


importlib.import_module(name, package=None)


Import a module. The name argument specifies what module to import in absolute or relative terms (e.g. either pkg.mod or ..mod). If the name is specified in relative terms, then the package argument must be set to the name of the package which is to act as the anchor for resolving the package name (e.g. import_module('..mod', 'pkg.subpkg') will import pkg.mod).


e.g.

my_module = importlib.import_module('os.path')
Ответ 3

Note: imp is deprecated since Python 3.4 in favor of importlib


As mentioned the imp module provides you loading functions:

imp.load_source(name, path)
imp.load_compiled(name, path)

I've used these before to perform something similar.

In my case I defined a specific class with defined methods that were required.
Once I loaded the module I would check if the class was in the module, and then create an instance of that class, something like this:

import imp
import os

def load_from_file(filepath):
class_inst = None
expected_class = 'MyClass'

mod_name,file_ext = os.path.splitext(os.path.split(filepath)[-1])

if file_ext.lower() == '.py':
py_mod = imp.load_source(mod_name, filepath)

elif file_ext.lower() == '.pyc':
py_mod = imp.load_compiled(mod_name, filepath)

if hasattr(py_mod, expected_class):
class_inst = getattr(py_mod, expected_class)()

return class_inst
Ответ 4

Using importlib

Importing a source file

Here is a slightly adapted example from the documentation:

import sys
import importlib.util

file_path = 'pluginX.py'
module_name = 'pluginX'

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

# Verify contents of the module:
print(dir(module))

From here, module will be a module object representing the pluginX module (the same thing that would be assigned to pluginX by doing import pluginX). Thus, to call e.g. a hello function (with no parameters) defined in pluginX, use module.hello().

To get the effect "importing" functionality from the module instead, store it in the in-memory cache of loaded modules, and then do the corresponding from import:

sys.modules[module_name] = module

from pluginX import hello
hello()

Importing a package

To import a package instead, calling import_module is sufficient. Suppose there is a package folder pluginX in the current working directory; then just do

import importlib

pkg = importlib.import_module('pluginX')

# check if it's all there..
print(dir(pkg))
python