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


760

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

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

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

dirFoo\
    Foo.py
    dirBar\
        Bar.py

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


2
Похоже на stackoverflow.com/questions/72852/… , может быть?
Joril

3
Посмотрите мой ответ, он пока самый полный, другие не работают в особом случае, например, когда вы вызываете скрипт из другого каталога или из другого скрипта python. См stackoverflow.com/questions/279237/...
Сорин

У меня была похожая проблема, и я нашел это, и это работает !! apt-get install python-profiler
ldcl289

5
На всякий случай, если кто-то хочет сделать это статически и попадает сюда (как я это сделал :), вы также можете установить переменную окружения PYTHONPATH
okaram

Лучше всего следовать инструкциям в lib / site.py для каждого случая
Avenida Gez

Ответы:


333

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


5
я могу получить объяснение того, как это работает? У меня похожая проблема, и я ЛЮБЛЮ, чтобы заставить DIR-модуль Python вместо поиска
Карл Кротт

Запуск Win 7 Pro 64x и Python 2.7 я получаю несколько ошибок. 1) Пришлось добавить инспекцию в список импорта. 2) 1-е значение [0] в кортеже является пустой строкой. Второй, [1], показывает имя файла. Я предполагаю, что первым должен быть путь ... Есть идеи?
Адам Льюис

2
Если вы собираетесь в подкаталог, использовать его как это: os.path.realpath(os.path.abspath(os.path.split(inspect.getfile( inspect.currentframe() ))[0]) + "/subfolder")Do НЕ добавить вложенный перед тем, abspathкак это вызывает серьезные ошибки.
Максимилиан Хилс

4
@ scr4ve, вместо этого вы должны использовать os.path.join (), и вы можете добавить case ( cmd_subfolder) прямо к моему ответу. Спасибо!
сорин

7
для меня realpathуже генерируются абсолютные пути, поэтому я не нуждаюсь abspath. Также os.path.dirnameможет использоваться вместо split, что делает индексирование [0]устаревшим. Строка будет:os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))
Тед

328

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


204
Обратите внимание, что этот файл может быть полностью пустым.
Harley Holcombe

47
Если родительского каталога dirBar нет, sys.pathто присутствие __init__.pyв этом dirBarкаталоге мало помогает.
JFS

7
-1, добавление __init.py__будет работать только тогда, когда каталог уже находится в sys.path, а в моем случае это не так. Решение "сорина" (принятого) всегда работает.
Чаре Томчак

12
msgstr "когда каталог уже находится в sys.path". Хотя это совершенно верно, как мы можем догадаться, что каталог не был в sys.pathвопросе? Возможно, было что-то пропущено, что мы не видели или не знали?
S.Lott

4
Это ни в коем случае не означает ответ на вопрос: есть ли файл инициализации, это не заставляет python заглядывать в подкаталоги. Как это получило сотни голосов?
gented

261

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

import sys
sys.path.insert(0, <path to dirFoo>)
import Bar

6
Похоже, что ваш ответ не работает с относительными путями, см. Stackoverflow.com/questions/279237/…
Богдан

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

12
Вы можете сделать что-то вродеsys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Falko

илиsys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
ВИМ

1
Подумайте об использовании, так sys.path.insert(0, <path to dirFoo>)как он будет загружать этот модуль до того, как модули с тем же именем будут сохранены в другом месте.
Антон Тарасенко

117
import os
import sys
lib_path = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'lib'))
sys.path.append(lib_path)

import mymodule

2
Мне это нравится, потому что у вас есть возможность перейти в каталог.
Чарльз Л.

21
Вы должны использовать os.path.join()вместо объединения символ '/', который сломается в (хромых) окнах.
0xc0de

18
Это не надежно Это зависит от текущего рабочего каталога, а не от каталога, в котором находится сценарий.
Джеймсдлин

Может захотеть добавить только если путь еще не в пути. lib_path = os.path.abspath ('../ functions'), если lib_path отсутствует в sys.path: sys.path.append (lib_path)
Brent

в ответ на @jamesdlin, сочетающий несколько ответов: как насчет os.path.abspath(os.path.join(__file__,'..','lib'))?
Мэтью Дэвис

107

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

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

lib/abc.py

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

__init__.py

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

from lib.abc import <Your Module name>

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


79

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

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это просто пустой файл.


Также отметим, что init .py импортируется, когда импортируется первый модуль внутри этого пакета. Кроме того, ваш пример будет работать, только если src находится в site_packages (или в пути поиска)
Tom Leys

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

Я попробовал то же самое и потерпел неудачу. Я не знаю, почему. ImportError: Нет модуля с именем customMath
Мин Ли

9
Зачем кому-то хотеть импортировать Foo из самого Foo.py. Должно быть из Bar.py, я думаю.
bhaskarc

from dirFoo import Fooсказать Foo.bla(). Если использовать import dirFoo.Fooодин должен использовать dirFoo.Foo.bla()- довольно некрасиво, даже без случая верблюда.
Сис Тиммерман

45

Самый простой способ - использовать 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)

Обратите внимание, что в документации imp.load_source и imp.load_compiled перечислены как устаревшие. Вместо этого рекомендуется использовать imp.find_module и imp.load_module.
amicitas

@amicitas, Можете ли вы предоставить какую-либо ссылку для этого (мне это нужно, и я использую Python 2.6. Я знаю, что 2.7 документы говорят об этом, но не смог найти никаких ссылок относительно 2.6)
0xc0de

@ 0xc0de Вы можете найти это утверждение в документации для модуля imp как для python 2.7.3, так и для python 2.6.7 . Похоже, что эти функции даже не в документации по Python 3.2.
amicitas

1
Для импорта исходных файлов, подобных этому, в python 3.3, см. Импорт исходного файла python. (Python 3.3+) - переполнение стека . Спасибо, ребята за советы здесь.
nealmcb


23

Самый простой способ без каких-либо изменений в вашем скрипте - установить переменную окружения 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']

13

На мой взгляд, лучший выбор - поместить __ init __.py в папку и вызвать файл с

from dirBar.Bar import *

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


странно, что from dirBar.Bar import *работает, но нет from dirBar.Bar import Bar. ты знаешь почему * работает? Что делать, если у меня было несколько файлов в dirBar /, и я хотел получить только несколько из них (используя метод, подобный тому, который вы опубликовали здесь)?
тестер

2
@tester: используйте from dirBar import Bar.
Нобар

@tester это потому, что fromуказывает на источник, а все, что после, import- это то, что нужно взять из этого источника. from dirBar.Bar import Barозначает «Из источника, импортируйте сам источник», что не имеет смысла. что *хотя средства, «дают мне все , от источника»
FuriousFolder

11

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

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

ln -s (path)/module_name.py

или

ln -s (path)/package_name

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

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


10
from .dirBar import Bar

вместо:

from dirBar import Bar

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


2
Я не смог сделать это на окнах. Это на Linux?
Габриэль

1
Это будет работать из скрипта, импортирующего Foo. То есть: main.py импортирует dirFoo.Foo. Если вы пытаетесь запустить Foo.py как скрипт, он потерпит неудачу. См stackoverflow.com/questions/72852/...
jgomo3

9

Для этого случая, чтобы импортировать 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.


7

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

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

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

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

5
Если dirBarуже есть пакет Python (существование dirBar/__init__.py), нет необходимости добавлять dirBarк sys.path, нет? Утверждение import Barот Foo.pyдолжно хватить.
Санта

5

Относительный пример 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

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


5

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

Решение:

У меня есть скрипт 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.


1
Мне пришлось использовать одну косую черту вместо двух обратных (то есть site.addsitedir(sys.path[0]+'/includes')) в моей первой простой программе на Python break_time.py: https://github.com/ltfschoen/PythonTest . Я использую систему: MacOS v10.11.5, Python 2.7.12, IDLE IDE 2.7.12, Tk 8.5.9
Люк Шоен

4

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

Пример:

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

код:

from Desktop.test import *

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


1
помеченный импорт не рекомендуется. см .: stackoverflow.com/questions/2386714/why-is-import-bad
axolotl

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

3

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

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

URL на самом деле выглядит как pypi.org/project/require.py , и обратите внимание, что его нужно установить через Pip.
Флэш Шеридан

1
@FlashSheridan Нет, это другой проект. Я удалил py-require, так как был уверен, что никто не использовал его, хотя я не думал об этом посте. Если вам все еще нужна require()функция, вы можете взглянуть на мой проект Node.py: github.com/nodepy/nodepy
Niklas R

2

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

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

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

1
Я не понимаю вашу логику здесь. это слишком сложно
transilvlad

0

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

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питона переводчик скажет 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

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


-5

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

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

1
Если OP собирался жестко закодировать путь, они могли бы просто вставить этот путь в PYTHONPATH. Я думаю, что дело в том, чтобы не жестко закодировать путь, поскольку он сломался бы где-нибудь еще.
Мэтью

-15

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

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


11
Супер неэффективно!
0xc0de

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