Импортировать модуль из относительного пути
Как импортировать модуль 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 инициализируется из этих мест:
- Каталог, содержащий входной скрипт (или текущий каталог).
- PYTHONPATH (список имен каталогов с тем же синтаксисом, что и для переменной оболочки PATH).
- Зависит от установки по умолчанию.
Просто беги:
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
команда.
Добавьте файл __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!")
Это, конечно, если вы не планируете собрать их вместе. Но если это так, вам все равно не нужны два отдельных файла.