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


1143

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


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 , другой вопрос обмена стека


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+


Мне нравится этот метод, но когда я получаю результат 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

Это не динамически.
— Шай Алон

Я попытался: 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) 

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


... и 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/')

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


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!

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

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


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


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)

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)

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/


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)

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

@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, см. Ссылку выше.


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


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()

1
Это решение не позволяет вам указать путь, который задает вопрос.
— Мика Смит
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.