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

1143

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

derfred
источник
21
Хороший и простой вопрос - и полезные ответы, но они заставляют меня задуматься о том, что случилось с мантрой питона «Есть один очевидный способ» сделать это… Это не похоже ни на один, ни на простой и очевидный ответ на него. Кажется до смешного хакерским и зависящим от версии для такой фундаментальной операции (и это выглядит и более раздутым в новых версиях ..)
Ингер

Ответы:

1283

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

Себастьян Риттау
источник
53
Если бы я знал пространство имен - 'module.name' - я бы уже использовал __import__.
Шридхар Ратнакумар
62
@SridharRatnakumar значение первого аргумента of imp.load_sourceтолько устанавливает .__name__возвращаемый модуль. это не влияет на загрузку.
Дэн Д.
17
@DanD. - первый аргумент imp.load_source()определяет ключ новой записи, созданной в sys.modulesсловаре, поэтому первый аргумент действительно влияет на загрузку.
Брэндон Роудс
9
impМодуль является устаревшим , начиная с версии 3.4: impпакет находится на рассмотрении Deprecation в пользу importlib.
Чиль тен Бринке
9
@AXO и, более того, интересно, почему что-то столь простое и простое, как это , должно быть таким сложным. Это не во многих других языках.
скалистый
424

Преимущество добавления пути к 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
Дэрил Спитцер
источник
13
Как мы используем, sys.path.appendчтобы указать на один файл Python вместо каталога?
Фани
28
:-) Возможно, ваш вопрос лучше подойдет как вопрос StackOverflow, а не как комментарий к ответу.
Дэрил Спитцер
3
Путь Python может содержать zip-архивы, «яйца» (сложный вид zip-архивов) и т. Д. Из них можно импортировать модули. Таким образом, элементы пути действительно являются контейнерами файлов, но они не обязательно являются каталогами.
Алексис
12
Остерегайтесь того факта, что Python кэширует операторы импорта. В редком случае, когда у вас есть две разные папки с общим именем класса (classX), подход добавления пути к sys.path, импорта classX, удаления пути и повторения оставшихся путей не будет работать. Python всегда загружает класс с первого пути из своего кэша. В моем случае я стремился создать систему плагинов, в которой все плагины реализуют определенный класс X. Я в конечном итоге с помощью SourceFileLoader , заметьте, что его устаревание противоречиво .
ComFreek
3
Обратите внимание, что этот подход позволяет импортированному модулю импортировать другие модули из того же каталога, что часто делают модули, в то время как подход принятого ответа - нет (по крайней мере, в 3.7). importlib.import_module(mod_name)здесь можно использовать вместо явного импорта, если имя модуля неизвестно во время выполнения, но я бы добавил sys.path.pop()в конце, хотя, предполагая, что импортированный код не пытается импортировать больше модулей, чем он используется.
Eli_B
43

Если ваш модуль верхнего уровня не является файлом, но упакован как каталог с помощью __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 первого ", как обсуждалось здесь .

Сэм Грондаль
источник
Большое спасибо! Этот метод позволяет относительный импорт между подмодулями. Большой!
tebanep
Этот ответ соответствует документации здесь: docs.python.org/3/library/… .
Тим Людвински
1
а что есть mymodule?
Гульзар
@Gulzar, это то имя, которое вы хотели бы дать своему модулю, так что вы можете позже сделать это: «из mymodule import myclass»
Idodo
Так ... /path/to/your/module/на самом деле /path/to/your/PACKAGE/? а mymoduleты имеешь ввиду myfile.py?
Гульзар
37

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

Временно

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

Постоянно

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

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

Кредит / Источник: saarrrr , другой вопрос обмена стека

Miladiouss
источник
3
Это временное решение - отличный ответ, если вы хотите создать проект в блокноте Jupyter в другом месте.
Форди
Но ... опасно вмешиваться в путь
Шай Алон
@ShaiAlon Вы добавляете пути, поэтому никакой опасности, кроме случаев передачи кодов с одного компьютера на другой, пути могут испортиться. Поэтому для разработки пакетов я импортирую только локальные пакеты. Кроме того, имена пакетов должны быть уникальными. Если вы беспокоитесь, используйте временное решение.
Miladiouss
28

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

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

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

ncoghlan
источник
Мне нравится этот метод, но когда я получаю результат run_path, это словарь, к которому я не могу получить доступ?
Стивен Эллвуд,
Что вы подразумеваете под "не может получить доступ"? Вы не можете импортировать из него (вот почему это хороший вариант, когда доступ в стиле импорта не требуется), но содержимое должно быть доступно через обычный API dict ( result[name], result.get('name', default_value)и т. Д.)
ncoghlan
Этот ответ недооценен. Это очень коротко и просто! Еще лучше, если вам нужно правильное пространство имен модуля, вы можете сделать что-то вроде from runpy import run_path; from argparse import Namespace; mod = Namespace(**run_path('path/to/file.py'))
RuRo
20

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

Грязно, но это работает.

configfile = '~/config.py'

import os
import sys

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

import config
ctcherry
источник
Это не динамически.
Шай Алон
Я попытался: config_file = 'setup-for-chats', setup_file = get_setup_file (config_file + ".py"), sys.path.append (os.path.dirname (os.path.expanduser (setup_file))), импортировать config_file >> «ImportError: нет модуля с именем config_file»
Шай Алон,
17

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

load_source(module_name, path_to_file) 

метод из имп модуля .

Zuber
источник
... и imp.load_dynamic(module_name, path_to_file)для DLL
HEKTO
34
Heads, что чертенок устарела сейчас.
t1m0
13
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')
Крис Каллоуэй
источник
37
Зачем писать 14 строк глючного кода, если стандартная библиотека уже решает эту проблему? Вы не провели проверку ошибок в формате или содержимом full_path_to_module или других операций; и использование универсального except:предложения редко является хорошей идеей.
Крис Джонсон
Вы должны использовать больше "try-finally" здесь. Напримерsave_cwd = os.getcwd() try: … finally: os.chdir(save_cwd)
Кей - SE злой
11
@ChrisJohnson this is already addressed by the standard libraryда, но у питона есть неприятная привычка не быть обратно-совместимым ... так как проверенный ответ говорит, что до и после 3.3 есть 2 разных способа. В этом случае я бы предпочел написать свою собственную универсальную функцию, чем проверять версию на лету. И да, может быть, этот код не слишком хорошо защищен от ошибок, но он показывает идею (которая есть os.chdir (), хотя я не думаю об этом), на основе которой я могу написать лучший код. Отсюда +1.
Sushi271
13

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

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

Сорин
источник
1
Этот ответ работал для меня, где load_sourceнет, потому что он импортирует скрипт и обеспечивает доступ скрипта к модулям и глобальным переменным во время импорта.
Klik
13

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

Безумный физик
источник
13

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

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

/foo/bar.py

Вы могли бы сделать:

import sys
sys.path[0:0] = ['/foo'] # puts the /foo directory at the start of your path
import bar
пшеница
источник
1
@Wheat Почему sys.path [0: 0] вместо sys.path [0]?
user618677
5
B / c sys.path [0] = xy перезаписывает первый элемент пути, тогда как путь [0: 0] = xy эквивалентен path.insert (0, xy)
dom0
2
хм path.insert сработал для меня, но трюк [0: 0] не сработал.
2013 года
11
sys.path[0:0] = ['/foo']
Кевин Эдвардс
6
Explicit is better than implicit.Так почему бы не sys.path.insert(0, ...)вместо sys.path[0:0]?
winklerrr
8

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

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

... но это должно сделать работу.

Matt
источник
6

Вы можете использовать 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!
bob_twinkles
источник
5

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

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

Abhimanyu
источник
4

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

Redlegjed
источник
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)
yoniLavi
источник
4

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

Если у нас есть сценарии в одном проекте, но в разных каталогах, мы можем решить эту проблему следующим способом.

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

ubershmekel
источник
1
os.chdir? (минимальное количество символов для подтверждения комментария).
ychaouche
Я потратил весь день на устранение ошибки импорта в сгенерированном pyinstaller exe. В конце концов, это единственное, что сработало для меня. Большое вам спасибо за это!
frakman1
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)
user10370
источник
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 *
user2760152
источник
1
Это хак, который я использовал, и это вызвало у меня некоторые проблемы. Одна из наиболее болезненных проблем заключалась в том, что у IDEA есть проблема, заключающаяся в том, что она не получает измененный код из ссылки, но пытается сохранить то, что, по ее мнению, находится там. Придерживаюсь условия гонки, когда последним спасают ... Я потерял приличную работу из-за этого.
Грипп
@Gripp не уверен, что я понимаю вашу проблему, но я часто (почти исключительно) редактирую свои скрипты на удаленном сервере со своего рабочего стола через SFTP с таким клиентом, как CyberDuck, и в этом случае попытка отредактируйте символический файл, вместо этого намного безопаснее редактировать исходный файл. Вы можете уловить некоторые из этих проблем, используя gitи проверив их, git statusчтобы убедиться, что ваши изменения в сценарии действительно возвращаются в исходный документ и не теряются в эфире.
user5359531
2

Я написал свою собственную глобальную и переносимую функцию импорта, основанную на 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, см. Ссылку выше.

Andry
источник
2

Есть пакет, который посвящен этому конкретно:

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), но может оказаться излишним в зависимости от размера вашего проекта.

Fny
источник
1

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

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


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

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

Андрей Кейно
источник
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)

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

атаксии
источник
Таким образом, вы модифицируете sys.path, что не подходит для каждого варианта использования.
bgusach
@bgusach Это может быть правдой, но в некоторых случаях это также желательно (добавление пути к sys.path упрощает процесс импорта нескольких модулей из одного пакета). Во всяком случае, если это не желательно, можно сразу после этого сделатьsys.path.pop()
Ataxias
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вообще, это делает свое дело.

Майкл Скотт Катберт
источник
-1

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