Імпортуйте модуль із відносного шляху


759

Як імпортувати модуль 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/… , можливо?
Йоріл

3
Перевірте мою відповідь, вона є найбільш повною поки що, інші не працюють в особливих випадках, наприклад, коли ви викликаєте скрипт з іншого каталогу або з іншого сценарію python. Дивіться stackoverflow.com/questions/279237/…
sorin

У мене була подібна проблема, і я виявив це, і це працює !! apt-get install python-
profiler

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 замість запуску пошуку
Карл Кротт

Запуск Win 7 Pro 64x та Python 2.7 я отримую кілька помилок. 1) Мені довелося додати інспектувати до списку імпорту. 2) Перше значення, [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 (), і ви можете додати справу ( cmd_subfolder) безпосередньо до моєї відповіді. Дякую!
sorin

7
для мене realpathвже генеруються абсолютні шляхи, тому мені це не потрібно abspath. Також os.path.dirnameможна використовувати замість спліт, роблячи індексацію [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
"коли каталог вже знаходиться в sys.path". Незважаючи на те, що це абсолютно правда, як ми могли здогадатися, що каталог не sys.pathвходить у запитання? Можливо, було пропущено щось, про що ми не бачили і не знали?
S.Lott

4
Це НЕ означає відповідь на питання: чи існує файл ініціалізації чи ні, це не змушує python шукати в підкаталогах. Як це отримало сотні оновлень?
покладено

261

Ви також можете додати підкаталог до шляху Python, щоб він імпортував як звичайний сценарій.

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

6
Здається, що ваша відповідь не працює з відносними шляхами, дивіться stackoverflow.com/questions/279237/…
bogdan

24
Це робить роботу з відносними шляхами - ви просто повинні зрозуміти , що відносний шлях буде залежати від того, в якому каталозі ви біжите з, що робить це погане рішення для будь-яких інших цілей, ніж швидкий хак.
nobar

12
Можна зробити щось на кшталтsys.path.append(os.path.dirname(__file__) + "/relative/path/to/module")
Фалько

абоsys.path.append(__name__ if __name__ != '__main__' else __loader__.fullname)
vim

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
Це не є надійним. Це залежить від того, який поточний робочий каталог є, а не від каталогу, в якому живе сценарій.
jamesdlin

Можливо, хочете додати лише те, якщо шлях ще не є. lib_path = os.path.abspath ('../ функції'), якщо 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 (або в шляху пошуку)
Том Лейс

3
Це найпростіше рішення, яке я шукав. Якщо файл для імпорту обов'язково знаходиться в одному з підкаталогів, це рішення є дорогоцінним каменем.
Deepak GM

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

9
Чому хтось хоче імпортувати Foo з самого Foo.py. Має бути з Bar.py, я думаю.
bhaskarc

from dirFoo import Fooсказати Foo.bla(). Якщо використовувати, import dirFoo.Fooви повинні використовувати dirFoo.Foo.bla()- досить некрасиво навіть без верблюда футляр.
Cees Timmerman

45

Найпростіший метод - використовувати sys.path.append ().

Однак вам може бути цікавий модуль імпульсів . Він забезпечує доступ до функцій внутрішнього імпорту.

# 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 Ви можете знайти це твердження в документах модуля імпульсів для 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.
nobar

@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

Якби я хотів, щоб пробіг імен виглядав як 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
Luke Schoen

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тоді я сказав, щоб полегшити його в * основному не добре для оперативної пам’яті, а також якщо дві функції мають таку ж назву, це збільшить ваш код
0x1996

3

Іншим рішенням буде встановити пакунок py- need, а потім використовувати наступне вFoo.py

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

URL-адреса насправді здається pypi.org/project/require.py , і зауважте, що її потрібно встановити через Pip.
Спалах Шерідан

1
@FlashSheridan Nope, це вже інший проект. Я видалив py-вимогу, оскільки я був певний, що ніхто не використовує її, але я не думав про цю посаду. Якщо вам все-таки потрібна require()функція, ви можете подивитися на мій проект Node.py
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
я не розумію тут тебе логіки. це над
сложно

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, інтерпретатор пітону скаже 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
Якби ОП збирав жорсткий код, він міг просто вставити цей шлях у ПІТОНПАТ. Я думаю, що суть це робиться таким чином, щоб не жорстко кодувати шлях, оскільки він би зламався деінде.
Метью

-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.