как импортировать подмодуль в Python (без exec)

Я хотел бы импортировать подмодуль, не зная заранее его имени,

>>> __import__("os.path")
<module 'os' from '/usr/lib/python3.3/os.py'>

Не работает должным образом, возвращает os, а не os.path.

Я придумал это решение.

def import_submodule(mod, submod):
    ns = {}
    exec_str = "from %s import %s as submod" % (mod, submod)
    exec(exec_str, ns, ns)
    return ns["submod"]

Это дает результат:

>>> import_submodule("os", "path")
<module 'posixpath' from '/usr/lib/python3.3/posixpath.py'>

Однако я бы предпочел не использовать exec (), потому что это довольно плохая практика и кажется ненужным, когда механизмы импорта Pythons доступны уже через модули __import__, imp и importlib.

Есть ли в Python3.x способ выполнить такой импорт через вызов функции, а не с помощью exec()?


person ideasman42    schedule 27.09.2013    source источник
comment
возможный дубликат Как динамически загрузить класс Python   -  person Martijn Pieters    schedule 27.09.2013
comment
Обратите внимание на цикл name.split('.'), then для использования getattr() для получения «подобъекта»; .path в вашем случае.   -  person Martijn Pieters    schedule 27.09.2013
comment
Использование в примерах os.path может привести вас к погоне за диким гусям, поскольку он не обязательно работает, как другие модули   -  person Marius    schedule 27.09.2013
comment
@Martijn Pieters, это не дубликат «Как динамически загружать класс Python». И использование getattr () в цикле не работает, модуль не всегда импортирует свои подмодули, в качестве примера возьмем пакеты с полностью пустым файлом __init__.py (что довольно часто). Суть моего вопроса заключается в том, чтобы эта работа работала точно так же, как from A import B as C.   -  person ideasman42    schedule 27.09.2013
comment
@ ideasman42: __import__('parentmodule.submodule') выполняет импорт submodule, но возвращает parentmodule.   -  person Martijn Pieters    schedule 27.09.2013
comment
@Martijn Pieters, верно, но потом вам нужно получить модуль каким-то другим способом. Вы бы посоветовали это как лучшую практику? my_import = lambda mod: (__import__(mod), __import__('sys').modules[mod])[1]   -  person ideasman42    schedule 27.09.2013
comment
@ ideasman42: См. связанный вопрос; разделите, затем назовите . и используйте все, кроме первого элемента в цикле с getattr().   -  person Martijn Pieters    schedule 27.09.2013
comment
Я отправил ответ, обратите внимание, что это работает для подмодуля, а не для других данных (например, классов в модулях), что является примером того, как этот вопрос отличается.   -  person ideasman42    schedule 27.09.2013
comment
@MartijnPieters Я считаю, что это не следует рассматривать как дубликат, поскольку OP явно нацелен на python3.3, для которого существует лучший ответ (т.е. importlib), который не цитируется в ответах на другой вопрос. Отметка этого как дубликата может означать, что люди по-прежнему будут использовать другие самодельные решения вместо нового импортного оборудования. В текущих версиях python вам почти никогда не нужно вызывать __import__ напрямую.   -  person Bakuriu    schedule 27.09.2013
comment
@Bakuriu: достаточно честно; Я тут удобно забыл про importlib.import_module().   -  person Martijn Pieters    schedule 27.09.2013


Ответы (2)


Используйте importlib.import_module:

>>> import importlib
>>> importlib.import_module('os.path')
<module 'posixpath' from '/usr/lib/python2.7/posixpath.pyc'>

Это должно работать в python2.7 + и 3.1+.

person Bakuriu    schedule 27.09.2013

Обратите внимание: если вы хотите do: from A import B as C как вызов функции, importlib.import_module не всегда будет работать, поскольку B может не быть модулем.

Вот функция, которая использует importlib и getattr.

def my_import_from(mod_name, var_name):
    import importlib
    mod = importlib.import_module(mod_name)
    var = getattr(mod, var_name)
    return var

Итак, это:

from os.path import dirname as var

Можно заменить на это:

var = my_import_from("os.path", "dirname")

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

Поскольку в моем вопросе явно говорится об импорте подмодуля, ответ от @Bakuriu верен, однако, включая его для полноты, он может помочь другим, которые сталкиваются с той же проблемой.

person ideasman42    schedule 28.09.2013