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

874

Как я могу загрузить модуль Python с учетом его полного пути? Обратите внимание, что файл может находиться где угодно в файловой системе, так как это параметр конфигурации.

Теги:
configuration
python-import
python-module

28 ответов

956
Лучший ответ

Для 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.

  • 35
    Если бы я знал пространство имен - 'module.name' - я бы уже использовал __import__ .
  • 50
    @SridharRatnakumar значение первого аргумента imp.load_source устанавливает только .__name__ возвращаемого модуля. это не влияет на загрузку.
Показать ещё 30 комментариев
317

Преимущество добавления пути к 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
  • 7
    Как мы используем sys.path.append для указания на один файл python вместо каталога?
  • 0
    @Daryl Spitzer: Как мы можем сделать это только для одного файла Python
Показать ещё 7 комментариев
19

Вы также можете сделать что-то подобное и добавить каталог, в котором находится файл конфигурации, в путь загрузки Python, а затем просто сделать обычный импорт, предполагая, что вы знаете имя файла заранее, в этом случае "конфигурации".

Беспокойно, но он работает.

configfile = '~/config.py'

import os
import sys

sys.path.append(os.path.dirname(os.path.expanduser(configfile)))

import config
16

Похоже, вы не хотите специально импортировать файл конфигурации (который содержит много побочных эффектов и дополнительных осложнений), вы просто хотите запустить его и иметь доступ к полученному пространству имен. Стандартная библиотека предоставляет API специально для этого в виде runpy.run_path:

from runpy import run_path
settings = run_path("/path/to/file.py")

Этот интерфейс доступен в Python 2.7 и Python 3.2 +

  • 0
    Мне нравится этот метод, но когда я получаю результат run_path, это словарь, к которому я не могу получить доступ?
  • 0
    Что вы подразумеваете под "не может получить доступ"? Вы не можете импортировать из него (вот почему это хороший вариант, когда доступ в стиле импорта не требуется), но содержимое должно быть доступно через обычный API dict ( result[name] , result.get('name', default_value) и т. д.)
16

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

load_source(module_name, path_to_file) 

из imp module.

  • 0
    ... и imp.load_dynamic(module_name, path_to_file) для DLL
  • 27
    Heads, что бес теперь устарела.
12
import sys
sys.path.append('/your/modul/path')
10

Вы имеете в виду загрузку или импорт?

Вы можете управлять списком sys.path, указывая путь к вашему модулю, а затем импортировать свой модуль. Например, с учетом модуля:

/foo/bar.py

Вы можете сделать:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
  • 0
    @Wheat Почему sys.path [0: 0] вместо sys.path [0]?
  • 4
    B / c sys.path [0] = xy перезаписывает первый элемент пути, в то время как путь [0: 0] = xy эквивалентен path.insert (0, xy)
Показать ещё 2 комментария
9
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:
        raise ImportError

import_file('/home/somebody/somemodule.py')
  • 36
    Зачем писать 14 строк с ошибочным кодом, если стандартная библиотека уже решает эту проблему? Вы не провели проверку ошибок в формате или содержимом full_path_to_module или других операций; и использование всеобъемлющего предложения, except: предложение редко бывает хорошей идеей.
  • 0
    Вы должны использовать больше "try-finally" здесь. Например, save_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
Показать ещё 1 комментарий
8

Я придумал слегка измененную версию замечательного ответа @SebastianRittau (для 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.

8

Вот код, который работает во всех версиях 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())

Я протестировал его. Это может быть уродливым, но до сих пор это единственное, что работает во всех версиях.

  • 0
    Этот ответ сработал для меня, когда load_source этого не сделал, потому что он импортирует скрипт и предоставляет скрипту доступ к модулям и глобальным переменным во время импорта.
8

Я считаю, что вы можете использовать imp.find_module() и imp.load_module() для загрузки указанного модуля. Вам нужно будет разбить имя модуля на путь, т.е. Если вы хотите загрузить /home/mypath/mymodule.py, вам нужно будет:

imp.find_module('mymodule', '/home/mypath/')

... но это должно быть выполнено.

6

Если ваш модуль верхнего уровня не является файлом, а упакован как каталог с __init__.py, принятое решение почти работает, но не совсем. В Python 3. 5+ необходим следующий код (обратите внимание на добавленную строку, начинающуюся с 'sys.modules'):

MODULE_PATH = "/path/to/your/module/__init__.py"
MODULE_NAME = "mymodule"
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: Parent module" mymodule "не загружен, не может выполнять относительный импорт". Поэтому вам нужно привязать имя до его загрузки. Причиной этого является фундаментальный инвариант относительной системы импорта: "Инвариантный холдинг состоит в том, что если у вас есть sys.modules ['spam'] и sys.modules ['spam.foo'] (как и после указанного импорта), последний должен появиться как атрибут foo первого ", как описано здесь.

4

Чтобы импортировать ваш модуль, вам необходимо добавить его каталог в переменную среды, временно или постоянно.

Временно

import sys
sys.path.append("/path/to/my/modules/")
import my_module

Постоянно

Добавив следующую строку в ваш .bashrc файл (в Linux) и excecute source ~/.bashrc в терминале:

export PYTHONPATH="${PYTHONPATH}:/path/to/my/modules/"

Кредит/Источник: saarrrr, еще один вопрос о стеке

  • 0
    Это временное решение - отличный ответ, если вы хотите продвинуть проект в ноутбуке Jupyter в другом месте.
3

Чтобы импортировать модуль из заданного имени файла, вы можете временно расширить путь и восстановить системный путь в блоке 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
3

Я не говорю, что это лучше, но для полноты я хотел предложить функцию 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)
  • 2
    execfile (), также
3

Эта область 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. Я не претендую на то, чтобы понять, почему, но, похоже, работает изнутри программы. Я нашел решение Криса работать в командной строке, но не изнутри программы.

3

Это должно работать

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)
  • 4
    Более общий способ вырезать расширение: name, ext = os.path.splitext(os.path.basename(infile)) . Ваш метод работает, потому что предыдущее ограничение на расширение .py. Кроме того, вам, вероятно, следует импортировать модуль в некоторую переменную / словарную запись.
2

В 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 *
  • 1
    Это хак, который я использовал, и это вызвало у меня некоторые проблемы. Одна из наиболее болезненных проблем заключалась в том, что у IDEA возникла проблема, заключающаяся в том, что она не воспринимает измененный код из ссылки, но пытается сохранить то, что, по ее мнению, находится там. Придерживаюсь условия гонки, когда последним спасается ... Я потерял приличную работу из-за этого.
  • 0
    @Gripp не уверен, что я понимаю вашу проблему, но я часто (почти исключительно) редактирую свои скрипты на удаленном сервере со своего рабочего стола через SFTP с таким клиентом, как CyberDuck, и в этом случае попытка отредактируйте символический файл, вместо этого намного безопаснее редактировать исходный файл. Вы можете уловить некоторые из этих проблем, используя git и проверив свой git status чтобы убедиться, что ваши изменения в сценарии фактически возвращаются в исходный документ и не теряются в эфире.
2

Вы можете использовать модуль 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 been imported!
2

Я сделал пакет, который использует 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/

  • 1
    os.chdir? (минимальное количество символов для подтверждения комментария).
  • 0
    Я потратил весь день на устранение ошибки импорта в сгенерированном pyinstaller exe. В конце концов, это единственное, что сработало для меня. Большое вам спасибо за это!
2

Импортировать модули пакета во время выполнения (рецепт 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)
1

Простое решение с использованием 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)

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

  • 0
    Таким образом, вы sys.path , который не подходит для каждого sys.path использования.
  • 0
    @bgusach Это может быть правдой, но в некоторых случаях это также желательно (добавление пути к sys.path упрощает процесс импорта нескольких модулей из одного пакета). В любом случае, если это не желательно, можно сразу же выполнить sys.path.pop()
1

довольно простой способ: предположим, что вы хотите импортировать файл с относительным путем. /../MyLibs/pyfunc.py


libPath = '../../MyLibs'
import sys
if not libPath in sys.path: sys.path.append(libPath)
import pyfunc as pf

Но если вы сделаете это без охраны, вы, наконец, сможете пройти очень длинный путь

0

Создать модуль 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

Мы можем импортировать импортированный модуль из модуля.

0

Этот ответ является дополнением к ответу Себастьяна Риттау, отвечая на комментарий: "но что, если у вас нет имени модуля?" Это быстрый и грязный способ получить вероятное имя модуля 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 в целом, это делает трюк.

0

Добавьте это в список ответов, поскольку я не мог найти ничего, что сработало. Это позволит импортировать скомпилированные (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()
0

Это может быть очевидно, но в интерактивной оболочке:

cd path
import module
-2

Лучший способ, я думаю, из официальной документации (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 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()
  • 1
    Это решение не позволяет вам указать путь, который задает вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню