Импортировать произвольный исходный файл Python. (Python 3.3+)
Как я могу импортировать произвольный исходный файл Python (имя которого может содержать любые символы, и не всегда заканчивается .py
) в Python 3.3+?
я использовал imp.load_module
следующее:
>>> import imp
>>> path = '/tmp/a-b.txt'
>>> with open(path, 'U') as f:
... mod = imp.load_module('a_b', f, path, ('.py', 'U', imp.PY_SOURCE))
...
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>
Это все еще работает в Python 3.3, но в соответствии с imp.load_module
Документация, устарела:
Устаревший с версии 3.3: Не требуется, так как загрузчики должны использоваться для загрузки модулей, а find_module() устарела.
а также imp
Модуль документации рекомендует использовать importlib
:
Примечание. Новые программы должны использовать importlib, а не этот модуль.
Как правильно загрузить произвольный исходный файл Python в Python 3.3+ без использования устаревших imp.load_module
функционировать?
5 ответов
Нашел решение от importlib
тестовый код.
Использование importlib.machinery.SourceFileLoader:
>>> import importlib.machinery
>>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
>>> mod = loader.load_module()
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>
ПРИМЕЧАНИЕ: работает только в Python 3.3+.
ОБНОВИТЬ Loader.load_module
устарела начиная с Python 3.4. использование Loader.exec_module
вместо:
>>> import types
>>> import importlib.machinery
>>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
>>> mod = types.ModuleType(loader.name)
>>> loader.exec_module(mod)
>>> mod
<module 'a_b'>
>>> import importlib.machinery
>>> import importlib.util
>>> loader = importlib.machinery.SourceFileLoader('a_b', '/tmp/a-b.txt')
>>> spec = importlib.util.spec_from_loader(loader.name, loader)
>>> mod = importlib.util.module_from_spec(spec)
>>> loader.exec_module(mod)
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>
Укороченная версия решения @falsetrue:
>>> import importlib.util
>>> spec = importlib.util.spec_from_file_location('a_b', '/tmp/a-b.py')
>>> mod = importlib.util.module_from_spec(spec)
>>> spec.loader.exec_module(mod)
>>> mod
<module 'a_b' from '/tmp/a-b.txt'>
Я проверил это с Python 3.5 и 3.6.
Согласно комментариям, он не работает с произвольными расширениями файлов.
Аналогично @falsetru, но для Python 3.5+ и учета того, что importlib
Док заявляет об использовании importlib.util.module_from_spec
над types.ModuleType
:
Эта функция [
importlib.util.module_from_spec
] предпочтительнее использованияtypes.ModuleType
создать новый модуль, так как спецификация используется для установки максимально возможного количества атрибутов, контролируемых импортом.
Мы можем импортировать любой файл с importlib
в одиночку, изменив importlib.machinery.SOURCE_SUFFIXES
список.
import importlib
importlib.machinery.SOURCE_SUFFIXES.append('') # empty string to allow any file
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# if desired: importlib.machinery.SOURCE_SUFFIXES.pop()
importlib
вспомогательная функция
Вот удобный, готовый к использованию помощник для замены
imp
, с примером. Техника такая же, как и у /questions/34391763/importirovat-proizvolnyij-ishodnyij-fajl-python-python-33/34391775#34391775, просто обеспечивает более удобную функцию.
main.py
#!/usr/bin/env python3
import os
import importlib
def import_path(path):
module_name = os.path.basename(path).replace('-', '_')
spec = importlib.util.spec_from_loader(
module_name,
importlib.machinery.SourceFileLoader(module_name, path)
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[module_name] = module
return module
notmain = import_path('not-main')
print(notmain)
print(notmain.x)
не-основной
x = 1
Бежать:
python3 main.py
Вывод:
<module 'not_main' from 'not-main'>
1
Я заменяю
-
с участием
_
потому что у моих импортируемых исполняемых файлов Python без расширения есть дефисы. Это не обязательно, но дает более точные имена модулей.
Этот шаблон также упоминается в документации по адресу: https://docs.python.org/3.7/library/importlib.html#importing-a-source-file-directly
В итоге я перешел на него, потому что после обновления до Python 3.7
import imp
печатает:
DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
и я не знаю, как это выключить, это спросили на:
Протестировано в Python 3.7.3.
после многих неудачных решений это работает для меня
def _import(func,*args):
import os
from importlib import util
module_name = "my_module"
BASE_DIR = "wanted module directory path"
path = os.path.join(BASE_DIR,module_name)
spec = util.spec_from_file_location(func, path)
mod = util.module_from_spec(spec)
spec.loader.exec_module(mod)
return getattr(mod,func)(*args)
и чтобы вызвать его, просто напишите имя функции и ее параметры
_import("function",*args)