Как я могу загрузить модуль Python, учитывая его полный путь? Обратите внимание, что файл может находиться в любом месте файловой системы, поскольку это параметр конфигурации.
@inger что произошло с мантрой питона «Есть один очевидный способ» сделать это [...] [не] единым или простым и очевидным ответом на него [...] смехотворно хакерским [...] более раздутым в новых версии Добро пожаловать в ужасный мир управления пакетами Python. Python import, virtualenv, pip, setuptools и многое другое следует выбросить и заменить рабочим кодом. Я просто попытался разобраться с virtualenv или это был pipenv, и мне пришлось работать с эквивалентом руководства по Jumbo Jet. То, как это изобретение преподносится как Решение проблемы депрессии, полностью ускользает от меня.
соответствующий XKCD xkcd.com/1987
@JohnFrazer усугубляет ситуацию из-за постоянного нытья людей, которые не удосужились прочитать 2 абзаца документации. Ваш XKCD на самом деле не имеет значения, поскольку он показывает, чего могут достичь эти люди, пытаясь что-то сделать, пока что-то не сработает. Кроме того, то, что появился новый способ, не означает, что теперь есть «два очевидных пути». Старый способ очевиден для одних случаев, новый способ упрощает использование в других. Вот что происходит, когда вы действительно заботитесь о DevX.
И подумайте, что Java или даже PHP (в наши дни) имеют ясный и простой способ разбивать вещи на пакеты / пространства имен и повторно использовать его. Удивительно видеть такую боль в Python, который принял простоту во всех остальных аспектах.






Я считаю, что вы можете использовать imp.find_module() и imp.load_module() для загрузки указанного модуля. Вам нужно будет отделить имя модуля от пути, т.е. если вы хотите загрузить /home/mypath/mymodule.py, вам нужно будет сделать:
imp.find_module('mymodule', '/home/mypath/')
... но на этом работа должна быть выполнена.
Для Python 3.5+ используйте:
import importlib.util
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
foo.MyClass()
Для Python 3.3 и 3.4 используйте:
from importlib.machinery import SourceFileLoader
foo = SourceFileLoader("module.name", "/path/to/file.py").load_module()
foo.MyClass()
(Хотя это устарело в Python 3.4.)
Для Python 2 используйте:
import imp
foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()
Существуют эквивалентные удобные функции для скомпилированных файлов Python и DLL.
См. Также http://bugs.python.org/issue21436.
Если бы я знал пространство имен - «module.name» - я бы уже использовал __import__.
@SridharRatnakumar значение первого аргумента imp.load_source устанавливает только .__name__ возвращаемого модуля. это не влияет на загрузку.
@DanD. - первый аргумент imp.load_source() определяет ключ новой записи, созданной в словаре sys.modules, поэтому первый аргумент действительно влияет на загрузку.
Есть ли идея, что это репликация: "" import module.name as foo "", если ваш рабочий каталог был / path / to ?? Иначе, что здесь foo ??
См. Новое (за декабрь 2015 г.) сообщение в разделе «Обсуждение ошибки» (bugs.python.org/issue21436#msg255901): появился третий новый способ загрузки модуля в Python 3.5 длиной в три строки!
Я не вижу, что SourceFileLoader устарел в документации 3.4+.
SourceFileLoader не является устаревшим, но SourceFileLoader.load_module () устарел.
Несмотря на использование в вопросе «импорта», вы, вероятно, захотите использовать runpy.run_path для выполнения кода Python в качестве формата конфигурации, а не динамически манипулировать системой импорта.
@AXO и многое другое, когда возникает вопрос, почему такой простой и простой элемент, как этот имеет, может быть таким сложным. Его нет на многих других языках.
Если вам нужна версия кода, которая работает во всех версиях Python, проверьте stackoverflow.com/a/37611448/99834
Я использую Python 3.5.2, и я обнаружил, что Только работает, если расширение файла .py
@Paolo Celati В Python 3.5+ Вы должны использовать importlib.import_module (имя_модуля). Некоторым это нравится. sys, path.append (путь_к_файлу) module = importlib.import_module (имя_модуля)
могу я узнать, почему здесь не упоминается importlib.import_module?
@ Mahesha999 Потому что importlib.import_module () не позволяет вам импортировать модули по имени файла, о чем и был исходный вопрос.
Для Python 3.5+, если /path/to/file.py неявно импортирует родственника (например, import bar для импорта /path/to/bar.py), решение выдает ModuleNotFoundError: No module named 'bar'. Как это исправить?
^ В итоге я спросил об этом в отдельном вопросе StackOverflow: stackoverflow.com/q/41861427/1490091
importlib.util.spec_from_file_location не будет импортировать файлы, которые не заканчиваются на .py = (
Я использую python 3.6.3 на Sierra 10.12.6 и использую вариант 1 (для python 3.5+). Код работает, но когда я запускаю строку foo.MyClass(), я получаю сообщение об ошибке AttributeError: module 'myFileName' has no attribute 'MyClass', где myFileName - это имя файла python, который я передаю первому аргументу importlib.util.spec_from_file_location("module.name", "/path/to/file.py"). Тем не менее, когда я закомментировал строку foo.MyClass(), сценарий без проблем выполняет импортированный сценарий. Не могли бы кто-нибудь объяснить, что делает foo.MyClass() в предложенном коде?
@ColeRobertson Эта строка - всего лишь пример, показывающий, что вам нужно префикс любого доступа к модулю с помощью foo. (или как вы называете эту переменную).
Упоминалось, что для Python 2 вместо lib следует использовать importlib, но я не вижу примера использования importlib для импорта модуля по пути. У кого-нибудь есть такой пример?
Этот верхний безопасен? Это кажется слишком близким к тому, чтобы просто «выполнить» все, что вам скажет пользователь. Разве не было бы безопаснее добавить каталог в sys.path, а затем запросить конкретный модуль, который вам нужен?
@setholopolus Ничто из этого не является безопасным, если вы используете ненадежный пользовательский ввод.
@SebastianRittau Думаю, вы правы, потому что даже если вы импортировали конкретный модуль по имени, они могли бы заменить его своим собственным модулем с тем же именем.
@SebastianRittau Тогда какой в настоящее время лучший способ для python 3.4?
Этот почти работал у меня, но когда я импортирую модуль, структурированный как каталог с _в этом_.py, мне нужна была дополнительная строка. Смотрите мой ответ ниже - надеюсь, это кому-то поможет!
Как упоминал @SamGrondahl, они не работают, если у модуля есть относительный импорт. Его отвечать предоставляет решение для python 3.5+: есть ли подобное решение для python 2.7?
Следует ли spec.loader.exec_module(foo) также гарантировать, что сценарий полностью запущен? У меня есть настройка, в которой я импортирую список переменных (и намеренно не класс), которые должны быть действительными и активными в сценарии, из которого я его вызываю. Однако, в отличие от обычного оператора import, мои переменные нельзя вызывать в дальнейшем.
@Lena Должно быть, и в моем коротком тесте так и было.
Остерегайтесь точек в имени файла, потому что последняя версия модуля python importlib все еще не может обрабатывать имена файлов модулей с точками: bugs.python.org/issue38000
Интересно, как код становится ДЛИННЕ и длиннее с каждой новой версией Python. Не уверен, что это действительно «питонический способ» разработки языка программирования: / (то же самое для других вещей, таких как print () и т. д.)
Python 2.7: TypeError: load_module () принимает ровно 4 аргумента
Если импортированный файл содержит какой-либо относительный импорт, эти решения не пройдут ImportError: attempted relative import with no known parent package
module.name - это тот модуль, с которого я запускаю?
Использование описанного метода v3.5 + приводит к ошибкам травления. Ответ, связанный выше @mforbes здесь, добавляет дополнительный шаг, который, как представляется, исправляет это: sys.modules[spec.name] = foo
Они что, не в своем уме создают 3 разных способа для одного и того же в этих разных языковых версиях ???
Что такое module.name ?? Я импортирую файл или что-то из файла. У него нет пространства имен.
Вы можете использовать
load_source(module_name, path_to_file)
метод из модуль imp.
... и imp.load_dynamic(module_name, path_to_file) для DLL
предупреждает, что черт возьми устарел.
Импортировать модули пакета во время выполнения (рецепт Python)
http://code.activestate.com/recipes/223972/
###################
## #
## classloader.py #
## #
###################
import sys, types
def _get_mod(modulePath):
try:
aMod = sys.modules[modulePath]
if not isinstance(aMod, types.ModuleType):
raise KeyError
except KeyError:
# The last [''] is very important!
aMod = __import__(modulePath, globals(), locals(), [''])
sys.modules[modulePath] = aMod
return aMod
def _get_func(fullFuncName):
"""Retrieve a function object from a full dotted-package name."""
# Parse out the path, module, and function
lastDot = fullFuncName.rfind(u".")
funcName = fullFuncName[lastDot + 1:]
modPath = fullFuncName[:lastDot]
aMod = _get_mod(modPath)
aFunc = getattr(aMod, funcName)
# Assert that the function is a *callable* attribute.
assert callable(aFunc), u"%s is not callable." % fullFuncName
# Return a reference to the function itself,
# not the results of the function.
return aFunc
def _get_class(fullClassName, parentClass=None):
"""Load a module and retrieve a class (NOT an instance).
If the parentClass is supplied, className must be of parentClass
or a subclass of parentClass (or None is returned).
"""
aClass = _get_func(fullClassName)
# Assert that the class is a subclass of parentClass.
if parentClass is not None:
if not issubclass(aClass, parentClass):
raise TypeError(u"%s is not a subclass of %s" %
(fullClassName, parentClass))
# Return a reference to the class itself, not an instantiated object.
return aClass
######################
## Usage ##
######################
class StorageManager: pass
class StorageManagerMySQL(StorageManager): pass
def storage_object(aFullClassName, allOptions = {}):
aStoreClass = _get_class(aFullClassName, StorageManager)
return aStoreClass(allOptions)
Вы также можете сделать что-то подобное и добавить каталог, в котором находится файл конфигурации, в путь загрузки Python, а затем просто выполнить обычный импорт, если вы заранее знаете имя файла, в данном случае «config».
Беспорядочно, но это работает.
configfile = '~/config.py'
import os
import sys
sys.path.append(os.path.dirname(os.path.expanduser(configfile)))
import config
Это не динамично.
Я пробовал: config_file = 'setup-for-chats', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_fil e))), import config_file >> "ImportError: Нет модуля с именем config_file"
Вы имеете в виду загрузку или импорт?
Вы можете манипулировать списком sys.path, указав путь к вашему модулю, а затем импортировать ваш модуль. Например, учитывая модуль по адресу:
/foo/bar.py
Вы могли сделать:
import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
@Wheat Почему sys.path [0: 0] вместо sys.path [0]?
B / c sys.path [0] = xy перезаписывает первый элемент пути, в то время как path [0: 0] = xy эквивалентен path.insert (0, xy)
хм, у меня path.insert сработал, а вот трюк [0: 0] - нет.
sys.path[0:0] = ['/foo']
Explicit is better than implicit. Так почему бы не sys.path.insert(0, ...) вместо sys.path[0:0]?
@ dom0 Тогда просто переходи с sys.path.append(...). Понятнее.
вы можете сделать это с помощью __ import __ и chdir
def import_file(full_path_to_module):
try:
import os
module_dir, module_file = os.path.split(full_path_to_module)
module_name, module_ext = os.path.splitext(module_file)
save_cwd = os.getcwd()
os.chdir(module_dir)
module_obj = __import__(module_name)
module_obj.__file__ = full_path_to_module
globals()[module_name] = module_obj
os.chdir(save_cwd)
except Exception as e:
raise ImportError(e)
return module_obj
import_file('/home/somebody/somemodule.py')
Зачем писать 14 строк кода с ошибками, если это уже решено стандартной библиотекой? Вы не выполнили проверку на ошибки в формате или содержимом full_path_to_module или других операций; а использование всеобъемлющего предложения except: редко бывает хорошей идеей.
Вы должны использовать здесь больше «попробуй наконец». Например. save_cwd = os.getcwd()try: …finally: os.chdir(save_cwd)
@ChrisJohnson this is already addressed by the standard library да, но у python неприятная привычка не быть обратно совместимой ... поскольку проверенный ответ говорит, что есть 2 разных способа до и после 3.3. В таком случае я бы предпочел написать свою универсальную функцию, чем проверять версию на лету. И да, возможно, этот код не слишком хорошо защищен от ошибок, но он показывает идею (это os.chdir (), я не думал об этом), на основе которой я могу написать лучший код. Следовательно, +1.
Было бы здорово, если бы это действительно вернуло модуль.
Преимущество добавления пути к sys.path (по сравнению с использованием imp) состоит в том, что это упрощает импорт более одного модуля из одного пакета. Например:
import sys
# the mock-0.3.1 dir contains testcase.py, testutils.py & mock.py
sys.path.append('/foo/bar/mock-0.3.1')
from testcase import TestCase
from testutils import RunTests
from mock import Mock, sentinel, patch
Как мы можем использовать sys.path.append, чтобы указать на один файл Python вместо каталога?
@ Дэрил Спитцер: Как это сделать для одного файла Python
Как мы используем это для одного файла Python? И почему ты 7 лет игнорируешь нас?
:-) Возможно, ваш вопрос лучше подошел бы как вопрос StackOverflow, а не как комментарий к ответу.
Для всех людей, которые пытались включить файл в свой путь ... по определению «путь оболочки - это список каталогов, разделенных двоеточиями». Я относительно новичок в python, но путь python также следует принципу дизайна unix из того, что я видел. Пожалуйста, поправьте меня, если я ошибаюсь.
Путь python может содержать zip-архивы, «яйца» (сложный вид zip-архивов) и т. д. Из них можно импортировать модули. Таким образом, элементы пути действительно являются контейнеры файлов, но это не обязательно каталоги.
Остерегайтесь того факта, что Python кэширует операторы импорта. В редком случае, когда у вас есть две разные папки, совместно использующие одно имя класса (classX), подход к добавлению пути к sys.path, импорту classX, удалению пути и повторению для новых путей не сработает. Python всегда будет загружать класс из своего кеша по первому пути. В моем случае я стремился создать систему плагинов, в которой все плагины реализуют определенный classX. В итоге я использовал SourceFileLoader, обратите внимание, что его Устаревшая является спорным.
Я предпочитаю это решение принятому, потому что оно все еще работает на Python 3.6 и потому, что для него требуется всего 2 простые строки кода, чтобы можно было найти любое количество модулей в другом каталоге сайта. Наша конкретная ситуация была на веб-сервере, где централизованно установлено только несколько основных пакетов, и таким образом сценарии CGI, созданные пользователями, могут быть импортированы из их каталога пользовательских сайтов. Спасибо!
В моем случае импортированный файл имел другой (транзитивный) импорт относительного пути; объединение принятого ответа (importlib) с этим сработало для меня.
Обратите внимание, что этот подход позволяет импортированному модулю импортировать другие модули из того же каталога, что часто делают модули, а подход принятого ответа - нет (по крайней мере, в 3.7). importlib.import_module(mod_name) можно использовать вместо явного импорта здесь, если имя модуля неизвестно во время выполнения, я бы добавил sys.path.pop() в конце, однако, предполагая, что импортированный код не пытается импортировать больше модулей по мере его использования.
Этот должен быть на высоте!
Я сделал для вас пакет, который использует imp. Я называю его import_file, и вот как он используется:
>>>from import_file import import_file
>>>mylib = import_file('c:\mylib.py')
>>>another = import_file('relative_subdir/another.py')
Вы можете получить его:
http://pypi.python.org/pypi/import_file
или в
http://code.google.com/p/import-file/
os.chdir? (минимальное количество символов для подтверждения комментария).
Я потратил весь день на устранение ошибки импорта в exe-файле, созданном pyinstaller. В конце концов, это единственное, что у меня сработало. Большое вам спасибо за это!
Это должно работать
path = os.path.join('./path/to/folder/with/py/files', '*.py')
for infile in glob.glob(path):
basename = os.path.basename(infile)
basename_without_extension = basename[:-3]
# http://docs.python.org/library/imp.html?highlight=imp#module-imp
imp.load_source(basename_without_extension, infile)
Более общий способ вырезания удлинителя: name, ext = os.path.splitext(os.path.basename(infile)). Ваш метод работает, потому что предыдущее ограничение на расширение .py. Кроме того, вам, вероятно, следует импортировать модуль в какую-либо запись переменной / словаря.
Вы можете использовать модуль pkgutil (в частности, метод walk_packages), чтобы получить список пакетов в текущем каталоге. Оттуда легко использовать оборудование importlib для импорта нужных модулей:
import pkgutil
import importlib
packages = pkgutil.walk_packages(path='.')
for importer, name, is_package in packages:
mod = importlib.import_module(name)
# do whatever you want with module now, it's been imported!
В Linux добавление символической ссылки в каталог, в котором находится ваш скрипт python, работает.
то есть:
ln -s /absolute/path/to/module/module.py /absolute/path/to/script/module.py
python создаст /absolute/path/to/script/module.pyc и обновит его, если вы измените содержимое /absolute/path/to/module/module.py
затем включите следующее в mypythonscript.py
from module import *
Это хак, который я использовал, и он вызвал у меня некоторые проблемы. Одним из наиболее болезненных моментов было то, что у IDEA есть проблема, когда он не берет измененный код из ссылки, но все же пытается сохранить то, что, по его мнению, существует. Состояние гонки, когда сохраняется последний, кто сохраняет ... Из-за этого я потерял приличный объем работы.
@Gripp не уверен, понимаю ли я вашу проблему, но я часто (почти исключительно) редактирую свои скрипты на удаленном сервере со своего рабочего стола через SFTP с таким клиентом, как CyberDuck, и в этом случае также плохая идея пытаться и отредактируйте файл, связанный с символической ссылкой, вместо этого гораздо безопаснее редактировать исходный файл. Вы можете выявить некоторые из этих проблем, используя git и проверяя свой git status, чтобы убедиться, что ваши изменения в скрипте действительно возвращаются в исходный документ и не теряются в эфире.
Думаю, лучший способ - из официальной документации (29.1. imp - Доступ к внутренним компонентам импорта):
import imp
import sys
def __import__(name, globals=None, locals=None, fromlist=None):
# Fast path: see if the module has already been imported.
try:
return sys.modules[name]
except KeyError:
pass
# If any of the following calls raises an exception,
# there's a problem we can't handle -- let the caller handle it.
fp, pathname, description = imp.find_module(name)
try:
return imp.load_module(name, fp, pathname, description)
finally:
# Since we may exit via an exception, close fp explicitly.
if fp:
fp.close()
Это решение не позволяет указать путь, о котором спрашивает вопрос.
Эта область Python 3.4 кажется чрезвычайно сложной для понимания! Однако, немного взломав, используя код Криса Кэллоуэя в качестве начала, мне удалось заставить что-то работать. Вот основная функция.
def import_module_from_file(full_path_to_module):
"""
Import a module given the full path/filename of the .py file
Python 3.4
"""
module = None
try:
# Get module name and path from full path
module_dir, module_file = os.path.split(full_path_to_module)
module_name, module_ext = os.path.splitext(module_file)
# Get module "spec" from filename
spec = importlib.util.spec_from_file_location(module_name,full_path_to_module)
module = spec.loader.load_module()
except Exception as ec:
# Simple error printing
# Insert "sophisticated" stuff here
print(ec)
finally:
return module
Похоже, что здесь используются нерекомендуемые модули из Python 3.4. Я не претендую на понимание почему, но, похоже, это работает изнутри программы. Я обнаружил, что решение Криса работает в командной строке, но не внутри программы.
Я не говорю, что это лучше, но для полноты картины я хотел предложить функцию exec, доступную как в Python 2, так и в 3.
exec позволяет выполнять произвольный код либо в глобальной области, либо во внутренней области, представленной в виде словаря.
Например, если у вас есть модуль, хранящийся в "/path/to/module "с функцией foo(), вы можете запустить его, выполнив следующие действия:
module = dict()
with open("/path/to/module") as f:
exec(f.read(), module)
module['foo']()
Это делает более явным, что вы загружаете код динамически, и дает вам некоторые дополнительные возможности, такие как возможность предоставлять настраиваемые встроенные функции.
И если для вас важен доступ через атрибуты, а не ключи, вы можете разработать собственный класс dict для глобальных переменных, который обеспечивает такой доступ, например:
class MyModuleClass(dict):
def __getattr__(self, name):
return self.__getitem__(name)
Чтобы импортировать модуль из заданного имени файла, вы можете временно расширить путь и восстановить системный путь в блоке finally ссылка:
filename = "directory/module.py"
directory, module_name = os.path.split(filename)
module_name = os.path.splitext(module_name)[0]
path = list(sys.path)
sys.path.insert(0, directory)
try:
module = __import__(module_name)
finally:
sys.path[:] = path # restore
Похоже, вы не хотите специально импортировать файл конфигурации (который имеет множество побочных эффектов и дополнительных сложностей), вы просто хотите запустить его и получить доступ к результирующему пространству имен. Стандартная библиотека предоставляет API специально для этого в форме runpy.run_path:
from runpy import run_path
settings = run_path("/path/to/file.py")
Этот интерфейс доступен в Python 2.7 и Python 3.2+.
Мне нравится этот метод, но когда я получаю результат run_path, это словарь, к которому я не могу получить доступ?
Что вы имеете в виду, говоря «нет доступа»? Вы не можете импортировать из него (поэтому это хороший вариант, когда доступ в стиле импорта на самом деле не требуется), но содержимое должно быть доступно через обычный dict API (result[name], result.get('name', default_value) и т. д.)
Этот ответ сильно недооценен. Это очень коротко и просто! Еще лучше, если вам нужно правильное пространство имен модуля, вы можете сделать что-то вроде from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
Привет, Ник. В PEP 338 вы ввели только функцию runpy.run_module и сказали, что python -m module_name теперь делегирует ей полномочия. 1. Вы писали аналогичный PEP для функции runpy.run_path? 2. Делегирует ли python file_path.py теперь runpy.run_path?
Кроме того, я только что открыл открытый вопрос здесь о вашем PEP 366. Мне очень интересно ваше мнение по этому поводу.
@Maggyero Командная строка никогда не проходит через runpy.run_path, но если заданный путь является каталогом или zip-файлом, он в конечном итоге делегирует runpy.run_module для выполнения __main__. Дублированная логика для "Это сценарий, каталог или zip-файл?" недостаточно сложен, чтобы его стоило делегировать коду Python.
Спасибо, я не знал об этом. В CPython происходит ли делегирование runpy.run_module для данного имени модуля (python -m name) точно здесь, а для данного каталога или пути к zip-файлу (python path) происходит точно здесь?
Также, глядя на выполнение функции C pymain_run_module, кажется, что CPython делегирует функцию Python runpy._run_module_as_main вместо runpy.run_module - хотя, если я правильно понял, единственная разница в том, что первая функция выполняет код во встроенной среде __main__ ( см. здесь), а вторая функция выполняет его в новой среде?
@Maggyero Ага, это единственная разница. Первоначально он использовал публичную функцию, но оказалось, что она плохо взаимодействует с опцией интерпретатора -i (которая сбрасывает вас в интерактивную оболочку в исходном модуле __main__, поэтому запуск -m в новом модуле был неудобным)
Вот код, который работает во всех версиях Python, начиная с 2.7–3.5 и, возможно, даже с другими.
config_file = "/tmp/config.py"
with open(config_file) as f:
code = compile(f.read(), config_file, 'exec')
exec(code, globals(), locals())
Я это проверил. Это может показаться некрасивым, но пока что единственный, который работает во всех версиях.
Этот ответ работал у меня там, где load_source не работал, потому что он импортирует скрипт и предоставляет скрипту доступ к модулям и глобальным объектам во время импорта.
Я придумал слегка измененную версию @ Замечательный ответ Себастьяна Риттау (думаю, для Python> 3.4), которая позволит вам загружать файл с любым расширением как модуль, используя spec_from_loader вместо spec_from_file_location:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("module.name", SourceFileLoader("module.name", "/path/to/file.py"))
mod = module_from_spec(spec)
spec.loader.exec_module(mod)
Преимущество кодирования пути в явном SourceFileLoader состоит в том, что машины не будет пытаться определить тип файла по расширению. Это означает, что вы можете загрузить что-то вроде файла .txt, используя этот метод, но вы не можете сделать это с spec_from_file_location без указания загрузчика, потому что .txt не находится в importlib.machinery.SOURCE_SUFFIXES.
Добавляю это в список ответов, так как я не мог найти ничего, что работало бы. Это позволит импортировать скомпилированные (pyd) модули python в 3.4:
import sys
import importlib.machinery
def load_module(name, filename):
# If the Loader finds the module name in this list it will use
# module_name.__file__ instead so we need to delete it here
if name in sys.modules:
del sys.modules[name]
loader = importlib.machinery.ExtensionFileLoader(name, filename)
module = loader.load_module()
locals()[name] = module
globals()[name] = module
load_module('something', r'C:\Path\To\something.pyd')
something.do_something()
довольно простой способ: предположим, вы хотите импортировать файл с относительным путем ../../MyLibs/pyfunc.py
libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf
Но если вы сделаете это без охраны, вы, наконец, сможете пройти очень долгий путь.
Если ваш модуль верхнего уровня не является файлом, а упакован как каталог с __init__.py, то принятое решение почти работает, но не совсем. В Python 3.5+ необходим следующий код (обратите внимание на добавленную строку, которая начинается с sys.modules):
MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
import importlib
import sys
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
Без этой строки, когда выполняется exec_module, он пытается привязать относительный импорт на вашем верхнем уровне __init__.py к имени модуля верхнего уровня - в данном случае «mymodule». Но «mymodule» еще не загружен, поэтому вы получите ошибку «SystemError: родительский модуль 'mymodule' не загружен, не может выполнить относительный импорт». Поэтому вам нужно привязать имя перед его загрузкой. Причиной этого является фундаментальный инвариант системы относительного импорта: «Инвариантное удержание заключается в том, что если у вас есть sys.modules ['spam'] и sys.modules ['spam.foo'] (как после вышеупомянутого импорта ), последний должен появиться как атрибут foo первого "как обсуждалось здесь.
Большое спасибо! Этот метод позволяет относительный импорт между подмодулями. Большой!
Этот ответ соответствует документации здесь: docs.python.org/3/library/….
а что такое mymodule?
@Gulzar, это любое имя, которое вы хотите дать своему модулю, чтобы потом вы могли сделать это: "from mymodule import myclass"
Итак ... /path/to/your/module/ на самом деле /path/to/your/PACKAGE/? а под mymodule вы имеете ввиду myfile.py?
Что такое mymodule по сравнению с /path/to/your/module/__init__.py?
Хотя это и нетрадиционно, но если ваша точка входа в пакет отличается от __init__.py, вы все равно можете импортировать ее как пакет. Включите spec.submodule_search_locations = [os.path.dirname(MODULE_PATH)] после создания спецификации. Вы также можете рассматривать __init__.py как не-пакет (например, одиночный модуль), задав для этого значения None.
Простое решение с использованием importlib вместо пакета imp (протестировано для Python 2.7, хотя оно должно работать и для Python 3):
import importlib
dirname, basename = os.path.split(pyfilepath) # pyfilepath: '/my/path/mymodule.py'
sys.path.append(dirname) # only directories should be added to PYTHONPATH
module_name = os.path.splitext(basename)[0] # '/my/path/mymodule.py' --> 'mymodule'
module = importlib.import_module(module_name) # name space of defined module (otherwise we would literally look for "module_name")
Теперь вы можете напрямую использовать пространство имен импортированного модуля, например:
a = module.myvar
b = module.myfunc(a)
Преимущество этого решения в том, что нам даже не нужно знать фактическое имя модуля, который мы хотим импортировать, чтобы использовать его в нашем коде. Это полезно, например, в случае, если путь к модулю является настраиваемым аргументом.
Таким образом вы модифицируете sys.path, что подходит не для всех вариантов использования.
@bgusach Это может быть правдой, но в некоторых случаях это также желательно (добавление пути к sys.path упрощает импорт более чем одного модуля из одного пакета). Во всяком случае, если это нежелательно, можно сразу после этого сделать sys.path.pop().
Этот ответ является дополнением к ответу Себастьяна Риттау на комментарий: «а что, если у вас нет имени модуля?» Это быстрый и грязный способ получить вероятное имя модуля python с учетом имени файла - он просто идет вверх по дереву, пока не найдет каталог без файла __init__.py, а затем преобразует его обратно в имя файла. Для Python 3.4+ (использует pathlib), что имеет смысл, поскольку люди Py2 могут использовать «imp» или другие способы выполнения относительного импорта:
import pathlib
def likely_python_module(filename):
'''
Given a filename or Path, return the "likely" python module name. That is, iterate
the parent directories until it doesn't contain an __init__.py file.
:rtype: str
'''
p = pathlib.Path(filename).resolve()
paths = []
if p.name != '__init__.py':
paths.append(p.stem)
while True:
p = p.parent
if not p:
break
if not p.is_dir():
break
inits = [f for f in p.iterdir() if f.name == '__init__.py']
if not inits:
break
paths.append(p.stem)
return '.'.join(reversed(paths))
Конечно, есть возможности для улучшения, и дополнительные файлы __init__.py могут потребовать других изменений, но если у вас есть __init__.py в целом, это поможет.
Чтобы импортировать модуль, вам необходимо временно или навсегда добавить его каталог в переменную среды.
import sys
sys.path.append("/path/to/my/modules/")
import my_module
Добавьте следующую строку в файл .bashrc (в Linux) и исключите source ~/.bashrc в терминале:
export PYTHONPATH = "${PYTHONPATH}:/path/to/my/modules/"
Кредит / Источник: Саарррр, еще один вопрос об обмене стеком
Это временное решение - отличный ответ, если вы хотите запустить проект в блокноте jupyter в другом месте.
Но ... это опасно вмешиваться в путь
@ShaiAlon Вы добавляете пути, поэтому нет никакой опасности, кроме передачи кодов с одного компьютера на другой, пути могут быть перепутаны. Итак, для разработки пакетов я импортирую только локальные пакеты. Кроме того, имена пакетов должны быть уникальными. Если вы беспокоитесь, воспользуйтесь временным решением.
Спасибо. Мне нравятся короткие, чистые, лаконичные ответы.
Создать модуль python test.py
import sys
sys.path.append("<project-path>/lib/")
from tes1 import Client1
from tes2 import Client2
import tes3
Создать модуль python test_check.py
from test import Client1
from test import Client2
from test import test3
Мы можем импортировать импортированный модуль из module.
Я написал свою собственную глобальную и переносимую функцию импорта, основанную на модуле importlib, для:
sys.path или любого хранилища путей поиска.Структура каталогов примеров:
<root>
|
+- test.py
|
+- testlib.py
|
+- /std1
| |
| +- testlib.std1.py
|
+- /std2
| |
| +- testlib.std2.py
|
+- /std3
|
+- testlib.std3.py
Зависимость и порядок включения:
test.py
-> testlib.py
-> testlib.std1.py
-> testlib.std2.py
-> testlib.std3.py
Выполнение:
Магазин последних изменений: https://sourceforge.net/p/tacklelib/tacklelib/HEAD/tree/trunk/python/tacklelib/tacklelib.py
test.py:
import os, sys, inspect, copy
SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\','/')
SOURCE_DIR = os.path.dirname(SOURCE_FILE)
print("test::SOURCE_FILE: ", SOURCE_FILE)
# portable import to the global space
sys.path.append(TACKLELIB_ROOT) # TACKLELIB_ROOT - path to the library directory
import tacklelib as tkl
tkl.tkl_init(tkl)
# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()
tkl_import_module(SOURCE_DIR, 'testlib.py')
print(globals().keys())
testlib.base_test()
testlib.testlib_std1.std1_test()
testlib.testlib_std1.testlib_std2.std2_test()
#testlib.testlib.std3.std3_test() # does not reachable directly ...
getattr(globals()['testlib'], 'testlib.std3').std3_test() # ... but reachable through the `globals` + `getattr`
tkl_import_module(SOURCE_DIR, 'testlib.py', '.')
print(globals().keys())
base_test()
testlib_std1.std1_test()
testlib_std1.testlib_std2.std2_test()
#testlib.std3.std3_test() # does not reachable directly ...
globals()['testlib.std3'].std3_test() # ... but reachable through the `globals` + `getattr`
testlib.py:
# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)
print("1 testlib::SOURCE_FILE: ", SOURCE_FILE)
tkl_import_module(SOURCE_DIR + '/std1', 'testlib.std1.py', 'testlib_std1')
# SOURCE_DIR is restored here
print("2 testlib::SOURCE_FILE: ", SOURCE_FILE)
tkl_import_module(SOURCE_DIR + '/std3', 'testlib.std3.py')
print("3 testlib::SOURCE_FILE: ", SOURCE_FILE)
def base_test():
print('base_test')
testlib.std1.py:
# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)
print("testlib.std1::SOURCE_FILE: ", SOURCE_FILE)
tkl_import_module(SOURCE_DIR + '/../std2', 'testlib.std2.py', 'testlib_std2')
def std1_test():
print('std1_test')
testlib.std2.py:
# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)
print("testlib.std2::SOURCE_FILE: ", SOURCE_FILE)
def std2_test():
print('std2_test')
testlib.std3.py:
# optional for 3.4.x and higher
#import os, inspect
#
#SOURCE_FILE = os.path.abspath(inspect.getsourcefile(lambda:0)).replace('\','/')
#SOURCE_DIR = os.path.dirname(SOURCE_FILE)
print("testlib.std3::SOURCE_FILE: ", SOURCE_FILE)
def std3_test():
print('std3_test')
Выход (3.7.4):
test::SOURCE_FILE: <root>/test01/test.py
import : <root>/test01/testlib.py as testlib -> []
1 testlib::SOURCE_FILE: <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE: <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE: <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE: <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE: <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib'])
base_test
std1_test
std2_test
std3_test
import : <root>/test01/testlib.py as . -> []
1 testlib::SOURCE_FILE: <root>/test01/testlib.py
import : <root>/test01/std1/testlib.std1.py as testlib_std1 -> ['testlib']
import : <root>/test01/std1/../std2/testlib.std2.py as testlib_std2 -> ['testlib', 'testlib_std1']
testlib.std2::SOURCE_FILE: <root>/test01/std1/../std2/testlib.std2.py
2 testlib::SOURCE_FILE: <root>/test01/testlib.py
import : <root>/test01/std3/testlib.std3.py as testlib.std3 -> ['testlib']
testlib.std3::SOURCE_FILE: <root>/test01/std3/testlib.std3.py
3 testlib::SOURCE_FILE: <root>/test01/testlib.py
dict_keys(['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', '__file__', '__cached__', 'os', 'sys', 'inspect', 'copy', 'SOURCE_FILE', 'SOURCE_DIR', 'TackleGlobalImportModuleState', 'tkl_membercopy', 'tkl_merge_module', 'tkl_get_parent_imported_module_state', 'tkl_declare_global', 'tkl_import_module', 'TackleSourceModuleState', 'tkl_source_module', 'TackleLocalImportModuleState', 'testlib', 'testlib_std1', 'testlib.std3', 'base_test'])
base_test
std1_test
std2_test
std3_test
Протестировано на Python 3.7.4, 3.2.5, 2.7.16
Плюсы:
testlib.std.py как testlib, testlib.blabla.py как testlib_blabla и т. д.).sys.path или от любого хранилища путей поиска.SOURCE_FILE и SOURCE_DIR, между вызовами tkl_import_module.3.4.x и выше] Может смешивать пространства имен модулей во вложенных вызовах tkl_import_module (например, named->local->named или local->named->local и т. д.).3.4.x и выше] Может автоматически экспортировать глобальные переменные / функции / классы, откуда они были объявлены, во все дочерние модули, импортированные через tkl_import_module (через функцию tkl_declare_global).Минусы:
3.3.x и ниже] Требовать объявления tkl_import_module во всех модулях, которые обращаются к tkl_import_module (дублирование кода)Обновление 1,2 (только для 3.4.x и выше):
В Python 3.4 и выше вы можете обойти требование объявлять tkl_import_module в каждом модуле, объявив tkl_import_module в модуле верхнего уровня, и функция будет внедряться во все дочерние модули за один вызов (это своего рода импорт для самостоятельного развертывания).
Обновление 3:
Добавлена функция tkl_source_module как аналог bash source с поддержкой защиты выполнения при импорте (реализована через слияние модулей вместо импорта).
Обновление 4:
Добавлена функция tkl_declare_global для автоматического экспорта глобальной переменной модуля во все дочерние модули, где глобальная переменная модуля не отображается, поскольку не является частью дочернего модуля.
Обновление 5:
Все функции перенесены в библиотеку tacklelib, см. Ссылку выше.
Специально для этого есть упаковка:
from thesmuggler import smuggle
# À la `import weapons`
weapons = smuggle('weapons.py')
# À la `from contraband import drugs, alcohol`
drugs, alcohol = smuggle('drugs', 'alcohol', source='contraband.py')
# À la `from contraband import drugs as dope, alcohol as booze`
dope, booze = smuggle('drugs', 'alcohol', source='contraband.py')
Он протестирован во всех версиях Python (в том числе Jython и PyPy), но может оказаться излишним в зависимости от размера вашего проекта.
Если у нас есть скрипты в одном проекте, но в разных каталогах, мы можем решить эту проблему следующим способом.
В этой ситуации utils.py находится в src/main/util/
import sys
sys.path.append('./')
import src.main.util.utils
#or
from src.main.util.utils import json_converter # json_converter is example method
самый простой ИМО
Это две мои служебные функции, использующие только pathlib. Он выводит имя модуля из пути По умолчанию он рекурсивно загружает все файлы Python из папок и заменяет в этом.py именем родительской папки. Но вы также можете указать путь и / или глобус для выбора некоторых конкретных файлов.
from pathlib import Path
from importlib.util import spec_from_file_location, module_from_spec
from typing import Optional
def get_module_from_path(path: Path, relative_to: Optional[Path] = None):
if not relative_to:
relative_to = Path.cwd()
abs_path = path.absolute()
relative_path = abs_path.relative_to(relative_to.absolute())
if relative_path.name == "__init__.py":
relative_path = relative_path.parent
module_name = ".".join(relative_path.with_suffix("").parts)
mod = module_from_spec(spec_from_file_location(module_name, path))
return mod
def get_modules_from_folder(folder: Optional[Path] = None, glob_str: str = "*/**/*.py"):
if not folder:
folder = Path(".")
mod_list = []
for file_path in sorted(folder.glob(glob_str)):
mod_list.append(get_module_from_path(file_path))
return mod_list
Вот способ загрузки файлов вроде C и т. д.
from importlib.machinery import SourceFileLoader
import os
def LOAD (MODULE_PATH):
if (MODULE_PATH [ 0 ] == "/"):
FULL_PATH = MODULE_PATH;
else:
DIR_PATH = os.path.dirname (os.path.realpath (__file__))
FULL_PATH = os.path.normpath (DIR_PATH + "/" + MODULE_PATH)
return SourceFileLoader (FULL_PATH, FULL_PATH).load_module ()
Реализации Где:
Y = LOAD ("../Z.py")
A = LOAD ("./A.py")
D = LOAD ("./C/D.py")
A_ = LOAD ("/IMPORTS/A.py")
Y.DEF ();
A.DEF ();
D.DEF ();
A_.DEF ();
Где каждый из файлов выглядит так:
def DEF ():
print ("A");
Я считаю, что это простой ответ: модуль = dict ()
code = """
import json
def testhi() :
return json.dumps({"key" : "value"}, indent = 4 )
"""
exec(code, module)
x = module['testhi']()
print(x)
Хороший и простой вопрос - и полезные ответы, но они заставляют меня задуматься о том, что случилось с мантрой python «Есть способ одиночевидный», чтобы сделать это .. Это не похоже на что-то вроде единственного или простого и очевидного ответа на него. Выглядит до смешного хакерским и зависящим от версии для такой фундаментальной операции (и в новых версиях он выглядит еще более раздутым ...).