Як посилатися на відносні шляхи ресурсів під час роботи з сховищем коду


188

Ми працюємо з сховищем коду, яке розгорнуто як для Windows, так і для Linux - іноді в різних каталогах. Як один з модулів всередині проекту повинен посилатися на один із ресурсів не-Python у проекті (файли CSV тощо)?

Якщо ми робимо щось на кшталт:

thefile=open('test.csv')

або:

thefile=open('../somedirectory/test.csv')

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

Я хотів би зробити щось на зразок:

path=getBasePathOfProject()+'/somedirectory/test.csv'
thefile=open(path)

Це можливо?

Відповіді:


255

Спробуйте використовувати ім’я файлу відносно поточного шляху файлів. Приклад для './my_file':

fn = os.path.join(os.path.dirname(__file__), 'my_file')

У Python 3.4+ ви також можете використовувати pathlib :

fn = pathlib.Path(__file__).parent / 'my_file'

3
Я думаю, що це рішення спрацює лише в тому випадку, якщо ресурс знаходиться в тому самому каталозі файлу python або в його підкаталозі. Як ви вирішите це, коли у вас є така структура дерева: / Project_Root_dir / python_files_dir / Деякі інші підкаталоги тут py_file.py / ресурси / деякі підкаталоги тут resource_file.csv
olamundo

1
Вибачте, дерево файлів отримано на цьому останньому повідомленні ... друга спроба: у вас є файл за адресою /Project_Root_dir/python_files_dir/some_subdirs/py_file.py, і у вас є ваш ресурсний файл за адресою /Project_Root_dir/resources/some_subdirs/resource_file.csv
olamundo

28
Ви повинні мати можливість дістатися до батьківського каталогу за допомогою приєднання (foo, '..'). Тож з / root / python_files / module / myfile, використовуйте os.path.join (os.path.dirname ( __file__), '..', '..', 'ресурси')
c089

7
os.pardirтрохи краще '..', хоча ці два еквіваленти як на POSIX, так і на Windows.
davidchambers

4
@cedbeu: Це еквівалентно кожній системі, на яку я коли-небудь стикався, і я думаю, що кожен системний пітон працює сьогодні (будь ласка, виправте мене, якщо я тут помиляюся). Однак, якщо ви очікуєте, що пітон буде перенесений до системи, використовуючи інший роздільник шляхів, і хочете, щоб ваш код був готовий до нього, os.pardir буде більш портативним. Я б робив так, що кожен програміст, навіть той, хто ніколи не читає жодного пітона, знає значення "..", тоді як "os.pardir" - це рівень непрямості, який потрібно було б шукати в документації так особисто " d дотримуйтесь "..".
c089

40

Якщо ви використовуєте інструменти налаштування або розповсюджуєте (встановлення setup.py), то, як видається, "правильним" способом доступу до цих запакованих ресурсів є використання пакет_ресурсів.

У вашому випадку прикладом був би

import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")

Що, звичайно, читає ресурс, а прочитані двійкові дані - це значення my_data

Якщо вам просто потрібна назва файлу, ви також можете використовувати

resource_filename(package_or_requirement, resource_name)

Приклад:

resource_filename("MyPackage","foo.dat")

Перевага полягає в тому, що він гарантовано працює, навіть якщо це архівний розподіл, як яйце.

Дивіться http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api


3
Я знаю, що це стара відповідь, моїм кращим способом є (/ можливо, це було?) Використовувати pkg_resources, але зі зникненням яйця-блискавки, чи є якась шкода в тому, як користуватися, __file__як у добрі старі часи?
Pykler

1
Це ґрунтовний підхід. Навіть якщо конвенція про яєць відходить, setuptools не є, і багато хто з них все ще встановлюють депи проти git repos, де яйце будується під час виконання
deepelement

18

У Python шляхи відносяться до поточного робочого каталогу , який у більшості випадків є каталогом, з якого ви запускаєте свою програму. Поточний робочий каталог , швидше за все , не так же , як і в директорії вашого файлу модуля, тому , використовуючи шлях щодо поточного файлу модуля завжди є поганим вибором.

Використання абсолютного шляху має бути найкращим рішенням:

import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')

15

Я часто використовую щось подібне до цього:

import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))

# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir') 

pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
    f = open(pathjoin(DATA_DIR, fn))
    # ...

Змінна

__file__

містить ім'я файлу сценарію, в який ви пишете цей код, тож ви можете робити шляхи відносно сценарію, але все-таки записані з абсолютними шляхами. Він працює досить добре з кількох причин:

  • шлях абсолютний, але все ще відносний
  • проект все ще може бути розгорнутий у відносному контейнері

Але вам потрібно стежити за сумісністю платформи - os.pathsep Windows відрізняється від UNIX.


5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)

Ви також намагаєтесь нормалізувати cwdвикористання os.path.abspath(os.getcwd()). Більше інформації тут .


3
дуже мало випадків використання, коли cwdшлях є модулем, хоча
cedbeu

він не працює всередині пакета, просто з того самого режиму (або робочого режиму), встановленого сценарієм.
Олександра

Це не спрацює, якщо користувач запускає програму, використовуючи абсолютний шлях з іншого каталогу. наприклад, python3 /usr/someone/test.py
sgrpwr

2

Ви можете використовувати збірку в __file__змінній. Він містить шлях поточного файлу. Я б реалізував getBaseOfProject в модулі в корені вашого проекту. Там я отримав би частину шляху __file__і повернув би її. Цей метод може бути використаний скрізь у вашому проекті.


0

Я тут трохи наткнувся. Хотіли упакувати деякі файли ресурсів у колесо і отримати доступ до них. Упаковка використовувала файл маніфесту, але програма pip install не встановлювала його, якщо це не підкаталог. Сподіваючись, ці кадри сцени допоможуть

├── cnn_client
   ├── image_preprocessor.py
   ├── __init__.py
   ├── resources
      ├── mscoco_complete_label_map.pbtxt
      ├── retinanet_complete_label_map.pbtxt
      └── retinanet_label_map.py
   ├── tf_client.py

MANIFEST.in

recursive-include cnn_client/resources *

Створений шматок за допомогою стандартного setup.py. pip встановив файл колеса. Після встановлення перевіряється, чи встановлені ресурси. Вони є

ls /usr/local/lib/python2.7/dist-packages/cnn_client/resources

mscoco_complete_label_map.pbtxt
retinanet_complete_label_map.pbtxt 
 retinanet_label_map.py  

У tfclient.py для доступу до цих файлів. з

templates_dir = os.path.join(os.path.dirname(__file__), 'resources')
 file_path = os.path.join(templates_dir, \
            'mscoco_complete_label_map.pbtxt')
        s = open(file_path, 'r').read()

І це працює.


-5

Я довгий час розбирався у відповіді на це, але я нарешті це зрозумів (і це насправді дуже просто):

import sys
import os
sys.path.append(os.getcwd() + '/your/subfolder/of/choice')

# now import whatever other modules you want, both the standard ones,
# as the ones supplied in your subfolders

Це додасть відносний шлях вашої папки до каталогів python, щоб заглянути в Це досить швидко і брудно, але це працює як шарм :)


6
Це буде працювати лише в тому випадку, якщо ви запускаєте програму Python з того самого каталогу, що і відповідний файл .py. І в такому випадку ви могли open('your/subfolder/of/choice')все одно зробити .
Пол Фішер

4
і ОП зазначила, що код повинен працювати і в Windows, і в Linux. Цього не буде.
користувач183037
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.