Подтвердить что ты не робот

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

Я пишу приложение 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)

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

Источник: 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()

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

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

4b9b3361

Ответ 1

С Python старше 2.7/3.1, это в значительной степени так, как вы это делаете.

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

Вы также можете использовать exec если хотите.

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

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

Разорвал прямо из Dive Into Python.

Ответ 2

Рекомендуемый способ для Python 2.7 и 3.1 и более поздних importlib - использовать модуль importlib:

importlib.import_module (name, package = None)

Импортируйте модуль. Аргумент name указывает, какой модуль следует импортировать в абсолютном или относительном выражении (например, pkg.mod или..mod). Если имя указано в относительных терминах, то аргумент пакета должен быть установлен на имя пакета, который должен выступать в качестве якоря для разрешения имени пакета (например, import_module ('.. mod', 'pkg.subpkg') будет импортировать pkg.mod).

например

my_module = importlib.import_module('os.path')

Ответ 3

Примечание: начиная с Python 3.4 imp устарела в пользу importlib

Как уже упоминалось, модуль Imp предоставляет вам функции загрузки:

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

Я использовал их раньше, чтобы выполнить нечто подобное.

В моем случае я определил определенный класс с определенными необходимыми методами. Как только я загрузил модуль, я бы проверил, есть ли класс в модуле, а затем создал бы экземпляр этого класса, что-то вроде этого:

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

Ответ 5

Если вы хотите этого у своих локальных жителей:

>>> mod = 'sys'
>>> locals()['my_module'] = __import__(mod)
>>> my_module.version
'2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]'

то же самое будет работать с globals()

Ответ 6

Вы можете использовать exec:

exec "import myapp.commands.%s" % command

Ответ 7

В настоящее время вы должны использовать importlib.

Импортировать исходный файл

Документы фактически предоставляют рецепт для этого, и он выглядит так:

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)

# check if it all there..
def bla(mod):
    print(dir(mod))
bla(module)

Импортировать пакет

Импортировать пакет (например, pluginX/__init__.py) под текущим каталогом на самом деле просто:

import importlib

pkg = importlib.import_module('pluginX')

# check if it all there..
def bla(mod):
    print(dir(mod))
bla(pkg)

Ответ 8

Аналогично решению @monkut, но многократно используемому и терпимому к ошибкам, описанному здесь http://stamat.wordpress.com/dynamic-module-import-in-python/:

import os
import imp

def importFromURI(uri, absl):
    mod = None
    if not absl:
        uri = os.path.normpath(os.path.join(os.path.dirname(__file__), uri))
    path, fname = os.path.split(uri)
    mname, ext = os.path.splitext(fname)

    if os.path.exists(os.path.join(path,mname)+'.pyc'):
        try:
            return imp.load_compiled(mname, uri)
        except:
            pass
    if os.path.exists(os.path.join(path,mname)+'.py'):
        try:
            return imp.load_source(mname, uri)
        except:
            pass

    return mod

Ответ 10

Следующий фрагмент работал у меня:

>>>import imp; 
>>>fp, pathname, description = imp.find_module("/home/test_module"); 
>>>test_module = imp.load_module("test_module", fp, pathname, description);
>>>print test_module.print_hello();

если вы хотите импортировать в shell- script:

python -c '<above entire code in one line>'

Ответ 11

Например, мои имена модулей похожи на jan_module/feb_module/mar_module.

month = 'feb'
exec 'from %s_module import *'%(month)

Ответ 12

Следующие работали для меня:

import sys, glob
sys.path.append('/home/marc/python/importtest/modus')
fl = glob.glob('modus/*.py')
modulist = []
adapters=[]
for i in range(len(fl)):
    fl[i] = fl[i].split('/')[1]
    fl[i] = fl[i][0:(len(fl[i])-3)]
    modulist.append(getattr(__import__(fl[i]),fl[i]))
    adapters.append(modulist[i]())

Он загружает модули из папки 'modus'. Модули имеют один класс с тем же именем, что и имя модуля. Например. файл modus/modu1.py содержит:

class modu1():
    def __init__(self):
        self.x=1
        print self.x

В результате получается список динамически загружаемых классов "адаптеры".