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

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

Например, если dirFoo содержит Foo.py а также dirBar, а также dirBar содержит Bar.pyкак мне импортировать Bar.py в Foo.py?

Вот визуальное представление:

dirFoo\
    Foo.py
    dirBar\
        Bar.py

Foo хочет включить Bar, но реструктуризация иерархии папок не вариант.

23 ответа

Решение

Предполагая, что обе ваши директории являются реальными пакетами Python (есть __init__.py файл внутри них), вот безопасное решение для включения модулей относительно места расположения скрипта.

Я предполагаю, что вы хотите сделать это, потому что вам нужно включить набор скриптов в ваш скрипт. Я использую это в производстве в нескольких продуктах и ​​работает во многих специальных сценариях, таких как: сценарии, вызываемые из другого каталога или выполняемые с использованием python, вместо открытия нового интерпретатора.

 import os, sys, inspect
 # realpath() will make your script run, even if you symlink it :)
 cmd_folder = os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]))
 if cmd_folder not in sys.path:
     sys.path.insert(0, cmd_folder)

 # Use this if you want to include modules from a subfolder
 cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"subfolder")))
 if cmd_subfolder not in sys.path:
     sys.path.insert(0, cmd_subfolder)

 # Info:
 # cmd_folder = os.path.dirname(os.path.abspath(__file__)) # DO NOT USE __file__ !!!
 # __file__ fails if the script is called in different ways on Windows.
 # __file__ fails if someone does os.chdir() before.
 # sys.argv[0] also fails, because it doesn't not always contains the path.

В качестве бонуса, этот подход позволяет вам заставить Python использовать ваш модуль вместо установленных в системе.

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

Убедитесь, что DirBar имеет __init__.py файл - это превращает каталог в пакет Python.

Вы также можете добавить подкаталог в путь Python, чтобы он импортировался как обычный скрипт.

import sys
sys.path.insert(0, <path to dirFoo>)
import Bar
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule

Просто сделайте простые вещи, чтобы импортировать файл.py из другой папки.

Допустим, у вас есть каталог вроде:

lib/abc.py

Затем просто сохраните пустой файл в папке lib с именем

__init__.py

А потом использовать

from lib.abc import <Your Module name>

Держать __init__.py файл в каждой папке иерархии модуля импорта.

Если вы структурируете свой проект следующим образом:

src\
  __init__.py
  main.py
  dirFoo\
    __init__.py
    Foo.py
  dirBar\
    __init__.py
    Bar.py

Тогда из Foo.py вы сможете сделать:

import dirFoo.Foo

Или же:

from dirFoo.Foo import FooObject

Согласно комментарию Тома, это требует, чтобы src папка доступна либо через site_packages или ваш путь поиска. Кроме того, как он упоминает, __init__.py импортируется неявно при первом импорте модуля в этот пакет / каталог. типично __init__.py это просто пустой файл.

Это соответствующий ПКП:

http://www.python.org/dev/peps/pep-0328/

В частности, предполагается, что dirFoo является каталогом из dirBar...

В dirFoo\Foo.py:

from ..dirBar import Bar

Самый простой способ - использовать sys.path.append().

Тем не менее, вы также можете быть заинтересованы в модуле Imp. Предоставляет доступ к внутренним функциям импорта.

# mod_name is the filename without the .py/.pyc extention
py_mod = imp.load_source(mod_name,filename_path) # Loads .py file
py_mod = imp.load_compiled(mod_name,filename_path) # Loads .pyc file 

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

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

Также эти функции могут быть полезны:

imp.find_module(name[, path])
imp.load_module(name, file, pathname, description)

Самый простой способ без каких-либо изменений в вашем скрипте - установить переменную окружения PYTHONPATH. Потому что sys.path инициализируется из этих мест:

  1. Каталог, содержащий входной скрипт (или текущий каталог).
  2. PYTHONPATH (список имен каталогов с тем же синтаксисом, что и для переменной оболочки PATH).
  3. Зависит от установки по умолчанию.

Просто беги:

export PYTHONPATH=/absolute/path/to/your/module

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

print sys.path

['', '/absolute/path/to/your/module', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client', '/usr/lib/python2.7/dist-packages/ubuntuone-client', '/usr/lib/python2.7/dist-packages/ubuntuone-control-panel', '/usr/lib/python2.7/dist-packages/ubuntuone-couch', '/usr/lib/python2.7/dist-packages/ubuntuone-installer', '/usr/lib/python2.7/dist-packages/ubuntuone-storage-protocol']

По моему мнению, лучший выбор - поместить __ init __.py в папку и вызвать файл с

from dirBar.Bar import *

Не рекомендуется использовать sys.path.append(), потому что что-то может пойти не так, если вы используете то же имя файла, что и существующий пакет Python. Я не проверял это, но это будет неоднозначно.

from .dirBar import Bar

вместо:

from dirBar import Bar

на всякий случай может быть установлен еще один dirBar, который запутает читателя foo.py.

Для этого случая, чтобы импортировать Bar.py в Foo.py, сначала я бы превратил эти папки в пакеты Python:

dirFoo\
    __init__.py
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Тогда я бы сделал это так в Foo.py:

from .dirBar import Bar

Если бы я хотел, чтобы пространство имен выглядело как Бар.что угодно, или

from . import dirBar

Если бы я хотел пространство имен dirBar.Bar.что угодно. Этот второй случай полезен, если у вас есть больше модулей в пакете dirBar.

Быстрый и грязный способ для пользователей Linux

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

ln -s (path)/module_name.py

или же

ln -s (path)/package_name

Примечание. "Модуль" - это любой файл с расширением.py, а "пакет" - любая папка, в которой находится файл. __init__.py (который может быть пустым файлом). С точки зрения использования модули и пакеты идентичны - оба предоставляют свои "определения и утверждения" в соответствии с запросом через import команда.

Смотрите: http://docs.python.org/2/tutorial/modules.html

Добавьте файл __init__.py:

dirFoo\
    Foo.py
    dirBar\
        __init__.py
        Bar.py

Затем добавьте этот код в начало Foo.py:

import sys
sys.path.append('dirBar')
import Bar

Относительный пример sys.path:

# /lib/my_module.py
# /src/test.py


if __name__ == '__main__' and __package__ is None:
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
import my_module

На основании этого ответа.

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

Решение:

У меня есть сценарий в D:/Books/MyBooks.py и некоторые модули (например, oldies.py). Мне нужно импортировать из подкаталога D:/Books/includes:

import sys,site
site.addsitedir(sys.path[0] + '\\includes')
print (sys.path)  # Just verify it is there
import oldies

Поместите print('done') в oldies.py, так что вы убедитесь, что все идет хорошо. Этот способ всегда работает, потому что по определению Python sys.path как инициализируется при запуске программы, первый элемент этого списка, path[0], каталог, содержащий скрипт, который использовался для вызова интерпретатора Python.

Если каталог скриптов недоступен (например, если интерпретатор вызывается в интерактивном режиме или если скрипт читается из стандартного ввода), path[0] является пустой строкой, которая направляет Python для поиска модулей в текущем каталоге. Обратите внимание, что каталог скриптов вставляется перед записями, вставленными в результате PYTHONPATH,

Другим решением было бы установить пакет py-require и затем использовать следующее в Foo.py

import require
Bar = require('./dirBar/Bar')

Просто вы можете использовать: from Desktop.filename import something

Пример:

учитывая, что файл является именем test.py в каталоге Users/user/Desktop и будет импортировать все.

код:

from Desktop.test import *

Но убедитесь, что вы делаете пустой файл с именем "__init__.py"в этом каталоге

Посмотрите на модуль pkgutil из стандартной библиотеки. Это может помочь вам сделать то, что вы хотите.

Вот способ импортировать файл с уровня выше, используя относительный путь.

По сути, просто переместите рабочий каталог на уровень (или любое другое относительное местоположение), добавьте его в свой путь, а затем переместите рабочий каталог туда, где он начался.

#to import from one level above:
cwd = os.getcwd()
os.chdir("..")
below_path =  os.getcwd()
sys.path.append(below_path)
os.chdir(cwd)

У меня нет опыта работы с Python, поэтому, если в моих словах что-то не так, просто скажите мне. Если ваша файловая иерархия устроена так:

project\
    module_1.py 
    module_2.py

module_1.py определяет функцию под названием func_1(), module_2.py:

from module_1 import func_1

def func_2():
    func_1()

if __name__ == '__main__':
    func_2()

и ты бежишь python module_2.py в CMD, он будет работать, что func_1() определяет. Обычно мы импортируем одни и те же файлы иерархии. Но когда ты пишешь from .module_1 import func_1 в module_2.py, интерпретатор Python скажет No module named '__main__.module_1'; '__main__' is not a package, Таким образом, чтобы исправить это, мы просто сохраняем изменения, которые мы просто вносим, ​​и перемещаем оба модуля в пакет, и делаем третий модуль вызывающим для запуска module_2.py,

project\
    package_1\
        module_1.py
        module_2.py
    main.py

main.py:

from package_1.module_2 import func_2

def func_3():
    func_2()

if __name__ == '__main__':
    func_3()

Но причина, по которой мы добавляем . до module_1 в module_2.py в том, что если мы этого не сделаем и не запустим main.py, интерпретатор Python скажет No module named 'module_1'Это немного сложно, module_1.py прямо рядом module_2.py, Теперь я позволю func_1() в module_1.py сделай что-нибудь:

def func_1():
    print(__name__)

тот __name__ записывает кто звонит func_1. Теперь мы держим . до module_1, бежать main.py, он напечатает package_1.module_1не module_1, Это указывает на то, что тот, кто звонит func_1() находится в той же иерархии, что и main.py, . подразумевать, что module_1 находится в той же иерархии, что и module_2.py сам. Так что, если нет точки, main.py узнает module_1 в той же иерархии, что и он сам, он может распознать package_1, но не то, что "под" этим.

Теперь давайте сделаем это немного сложнее. У тебя есть config.ini и модуль определяет функцию для чтения в той же иерархии, что и main.py.

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
    main.py

И по какой-то неизбежной причине, вы должны позвонить с module_2.py, поэтому он должен импортировать из верхней иерархии.module_2.py:

 import ..config
 pass

Две точки означают импорт из верхней иерархии (три точки - сверху, чем сверху и т. Д.). Теперь мы бежим main.pyпереводчик скажет:ValueError:attempted relative import beyond top-level package, "Пакет верхнего уровня" здесь main.py, Просто так config.py рядом main.pyони находятся в одной иерархии, config.py не "под" main.pyили он не "ведом" main.pyтак что это за main.py, Чтобы это исправить, самый простой способ:

project\
    package_1\
        module_1.py
        module_2.py
    config.py
    config.ini
main.py

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

Это также работает, и намного проще, чем что-либо с sys модуль:

with open("C:/yourpath/foobar.py") as f:
    eval(f.read())

Называйте меня чрезмерно осторожным, но мне нравится делать мой более переносимым, потому что небезопасно предполагать, что файлы всегда будут в одном и том же месте на каждом компьютере. Лично у меня есть код, сначала найдите путь к файлу. Я использую Linux, так что мой будет выглядеть так:

import os, sys
from subprocess import Popen, PIPE
try:
    path = Popen("find / -name 'file' -type f", shell=True, stdout=PIPE).stdout.read().splitlines()[0]
    if not sys.path.__contains__(path):
        sys.path.append(path)
except IndexError:
    raise RuntimeError("You must have FILE to run this program!")

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

Другие вопросы по тегам