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

Я пишу приложение на 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, localals = None, fromlist = (), level = 0)

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

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

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

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

19.11.2008 06:09:57
Что делает fromlist = ["myapp.commands"]?
Pieter Müller 6.06.2012 10:29:57
@ PieterMüller: в питона оболочки типа это: dir(__import__). Список должен быть списком имен для эмуляции "из импорта имен ...".
mawimawi 9.08.2012 07:13:14
По состоянию на 2019 год вы должны искать importlib: stackoverflow.com/a/54956419/687896
Brandt 13.03.2019 09:42:38
Не используйте __import__, смотрите Python Doc, используйте importlib.import_module
fcm 28.06.2019 15:00:19
11 ОТВЕТОВ
РЕШЕНИЕ

С 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 .

309
24.12.2018 07:53:08
в чем отличие exec?
user1767754 17.09.2014 07:10:20
Как вы могли бы использовать __init__этот модуль?
Dorian Dore 25.03.2015 03:00:16
Одна проблема с этим решением для OP состоит в том, что перехват исключений для одного или двух некорректных «командных» модулей приводит к сбою всего его / ее командного приложения в одном исключении. Лично я бы зациклился над каждым импортом, индивидуально обернутым в try: mods = __ import __ () \ nexcept ImportError как error: report (error), чтобы другие команды продолжали работать, пока исправляются плохие.
DevPlayer 8.04.2015 13:38:56
Другая проблема с этим решением, как указывает Денис Малиновский ниже, заключается в том, что сами документы по python рекомендуют не использовать __import__. Документы 2.7: «Поскольку эта функция предназначена для использования интерпретатором Python, а не для общего использования, лучше использовать importlib.import_module () ...» При использовании python3 impмодуль решает эту проблему, как упоминается ниже в monkut.
LiavK 8.07.2015 15:14:14
@JohnWu зачем тебе это нужно, foreachкогда есть for element inи map()хоть :)
cowbert 19.02.2018 22:59:13

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

exec "import myapp.commands.%s" % command
8
19.11.2008 06:12:54
Как получить дескриптор модуля через exec, чтобы я мог вызвать (в этом примере) метод .run ()?
Kamil Kisiel 19.11.2008 06:19:10
Затем вы можете выполнить getattr (myapp.commands, command) для доступа к модулю.
Greg Hewgill 19.11.2008 06:20:07
... или добавьте as command_moduleв конец оператора import, а затем сделайтеcommand_module.run()
oxfn 16.10.2013 10:43:34
В некоторых версиях Python 3+ exec был преобразован в функцию с дополнительным преимуществом, заключающимся в том, что результирующие ссылки, созданные в источнике, сохраняются в аргументе locals () функции exec (). Чтобы дополнительно изолировать exec, на который ссылаются локальные блоки кода, вы можете предоставить свой собственный dict, например пустой, и ссылаться на ссылки, используя этот dict, или передать этот dict другим функциям exec (source, gobals (), command1_dict). .... print (command1_dict ['somevarible'])
DevPlayer 8.04.2015 13:43:19
Это не работает в Python 3.7 может кто-нибудь предложить альтернативу этому
Ravinder Baid 2.08.2018 15:13:12

Используйте модуль imp или более прямую __import__()функцию.

17
19.11.2008 06:28:28
Не используйте __import__, смотрите Python Doc, используйте importlib.import_module
fcm 28.06.2019 15:01:27

Примечание: imp больше не поддерживается с Python 3.4 в пользу 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
130
26.05.2018 11:16:44
Хорошее и простое решение. Я написал аналогичный: stamat.wordpress.com/dynamic-module-import-in-python Но у вас есть некоторые недостатки: а исключения? IOError и ImportError? Почему бы не проверить сначала скомпилированную версию, а затем исходную версию. Ожидание класса уменьшает возможность повторного использования в вашем случае.
stamat 30.06.2013 20:40:16
В строке, где вы создаете MyClass в целевом модуле, вы добавляете избыточную ссылку на имя класса. Это уже хранится в, expected_classтак что вы могли бы сделать class_inst = getattr(py_mod,expected_name)()вместо этого.
Andrew 16.10.2013 07:11:24
Обратите внимание, что если существует соответствующий файл, скомпилированный байтами (с суффиксом .pyc или .pyo), он будет использоваться вместо анализа указанного исходного файла . https://docs.python.org/2/library/imp.html#imp.load_source
cdosborn 30.06.2015 21:07:00
Heads: это решение работает; тем не менее, модуль imp будет объявлен устаревшим в пользу импорта lib, см. страницу imp: "" "Устаревший с версии 3.4: пакет imp ожидает устаревания в пользу importlib." ""
Ricardo 20.11.2016 09:50:59

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

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

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

-7
20.08.2010 08:30:01
Пожалуйста, не хороните ответы без указания причины в комментариях. Это никому не поможет.
Rebs 11.06.2014 08:31:17
Я хотел бы знать, почему это плохо.
DevPlayer 1.09.2016 02:40:14
Так же, как и я, так как я новичок в Python и хочу знать, почему это плохо.
Kosmos 3.11.2016 17:50:39
Это плохо не только потому, что это плохой Python, но и вообще плохой код. Что такое fl? Определение цикла for является слишком сложным. Путь жестко запрограммирован (и, например, не имеет значения). Он использует обескураженный python ( __import__), для чего все это fl[i]? Это в основном нечитаемо и неоправданно сложно для чего-то, что не так уж сложно - посмотрите на голосующий ответ с одной строкой.
Phil 20.07.2017 05:07:22

Если вы хотите это в ваших местных жителей:

>>> 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()

11
6.11.2011 17:08:08
Документация для встроенной locals()функции явно предупреждает , что «содержание этого словаря не должно быть изменено».
martineau 14.01.2020 16:58:18

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

importlib.import_module (имя, пакет = нет)

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

например

my_module = importlib.import_module('os.path')
297
26.05.2018 11:38:52
Рекомендовано каким источником или органом?
michuelnik 8.04.2015 13:21:17
Документация советует против использования __import__функции в пользу упомянутого выше модуля.
Denis Malinovsky 9.04.2015 12:42:44
Это хорошо работает для импорта os.path; как насчет from os.path import *?
Nam G VU 12.06.2017 06:31:36
Я получаю ответ здесь stackoverflow.com/a/44492879/248616 т.е. вызоваglobals().update(my_module.__dict)
Nam G VU 12.06.2017 06:46:38
@NamGVU, это опасный метод, поскольку он загрязняет ваши глобальные переменные и может переопределять любые идентификаторы с одинаковыми именами. Ссылка, которую вы разместили, содержит улучшенную и улучшенную версию этого кода.
Denis Malinovsky 19.06.2017 17:11:14

Аналогично решению @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
3
30.06.2013 20:46:19

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

>>>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-скрипт:

python -c '<above entire code in one line>'
1
19.09.2017 19:32:13

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

month = 'feb'
exec 'from %s_module import *'%(month)
-2
1.10.2019 15:30:07

В настоящее время вы должны использовать 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's all there..
def bla(mod):
    print(dir(mod))
bla(module)

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

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

import importlib

pkg = importlib.import_module('pluginX')

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(pkg)
13
21.10.2019 15:56:44
добавьте sys.modules[module_name] = moduleв конец своего кода, если вы хотите иметь возможность import module_nameвпоследствии
Daniel Braun 15.04.2019 06:38:42