Как импортировать модуль по названию? - PullRequest
440 голосов
/ 19 ноября 2008

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

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

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

Ответы [ 12 ]

270 голосов
/ 19 ноября 2008

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

Разорвано прямо с Погружение в Python .

261 голосов
/ 22 декабря 2012

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

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

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

1012 *, например *

my_module = importlib.import_module('os.path')
126 голосов
/ 19 ноября 2008

Примечание: 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
19 голосов
/ 19 ноября 2008

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

13 голосов
/ 06 ноября 2011

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

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

10 голосов
/ 19 ноября 2008

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

exec "import myapp.commands.%s" % command
4 голосов
/ 02 марта 2019

В настоящее время вы должны использовать 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

pluginX = importlib.import_module('pluginX')

# check if it's all there..
def bla(mod):
    print(dir(mod))
bla(module)
3 голосов
/ 01 июля 2013

Похоже на решение @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
1 голос
/ 08 сентября 2018

для примера: мои имена модулей похожи на jan_module / feb_module / mar_module

месяц = ​​'февраля'
exec 'из% s_module import *'% (month)

1 голос
/ 19 ноября 2008

Похоже, что вы действительно хотите, это плагин архитектуры.

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

...