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

706 Jude Allred [2008-11-11 00:28:00]

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

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

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

dirFoo\
    Foo.py
    dirBar\
        Bar.py

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

python relative-path python-import


23 ответа


314 Решение sorin [2011-05-23 17:00:00]

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

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


317 S.Lott [2008-11-11 00:33:00]

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


245 Andrew Cox [2008-11-11 01:04:00]

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

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

110 lefakir [2010-11-26 13:21:00]

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

import mymodule

94 Deepak 'Kaseriya' [2010-12-09 13:40:00]

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

Скажем, у вас есть каталог вроде:

lib/abc.py

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

__init__.py

И затем используйте

from lib.abc import <Your Module name>

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


77 bouvard [2008-11-11 00:46:00]

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

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

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


44 monkut [2008-11-12 04:56:00]

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

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

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

Кроме того, эти функции могут быть полезны:

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

41 Peter Crabtree [2008-11-11 01:22:00]

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

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

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

В dirFoo\Foo.py:

from ..dirBar import Bar

20 James Gan [2013-02-11 02:21:00]

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

  • Каталог, содержащий вход script (или текущий каталог).
  • 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']

11 jhana [2010-09-15 08:02:00]

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

from dirBar.Bar import *

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


10 nobar [2012-12-20 04:07:00]

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

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

ln -s (path)/module_name.py

или

ln -s (path)/package_name

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

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


9 jgomo3 [2011-08-31 23:02:00]

from .dirBar import Bar

вместо:

from dirBar import Bar

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


7 Al Conrad [2014-10-29 22:49:00]

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

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

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

from .dirBar import Bar

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

from . import dirBar

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


6 Josh [2010-02-28 23:34:00]

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

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

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

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

5 Der_Meister [2014-07-29 10:27:00]

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

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


4 Avenida Gez [2015-04-02 01:29:00]

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

Решение:

У меня есть script в 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

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

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


3 Niklas R [2016-08-07 00:26:00]

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

следующее:
import require
Bar = require('./dirBar/Bar')

2 Mihail Mihaylov [2011-02-09 18:19:00]

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


2 0x1996 [2018-01-26 22:30:00]

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

Пример:

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

код:

from Desktop.test import *

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


2 Justin Muller [2012-01-27 20:43:00]

Здесь можно импортировать файл с одного уровня выше, используя относительный путь.

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

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

0 OmouYue [2017-11-09 17:38:00]

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

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

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


0 jgilley [2018-07-25 23:23:00]

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

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

-14 SuperFamousGuy [2011-09-22 22:33:00]

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

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