Как ссылаться на относительные пути ресурсов при работе с хранилищем кода


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 / resources / Некоторые подкаталоги здесь 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
Вы должны быть в состоянии добраться до родительского каталога, используя join (foo, '..'). Поэтому из / root / python_files / module / myfile используйте os.path.join (os.path.dirname ( __file__), '..', '..', 'resources')
— c089

7
os.pardirнемного лучше, чем '..', хотя оба они эквивалентны как для POSIX, так и для Windows.
— Дэвид Чэмберс

4
@cedbeu: Это эквивалентно на каждой системе, с которой я когда-либо сталкивался, и я думаю, что каждый системный питон работает сегодня (пожалуйста, поправьте меня, если я ошибаюсь здесь). Однако, если вы ожидаете, что Python будет перенесен в систему с использованием другого разделителя пути в будущем, и хотите, чтобы ваш код был к этому готов, os.pardir будет более переносимым. Я бы сказал, что каждый программист, даже тот, кто никогда не читает ни одного Python, знает значение «..», тогда как «os.pardir» - это уровень косвенности, который мне придется искать в документации, так что лично я буду придерживаться "..".
— c089

41

Если вы используете инструменты установки или распространяете (установка setup.py), тогда «правильный» способ получить доступ к этим упакованным ресурсам - использовать package_resources.

В вашем случае пример будет

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__как в старые добрые времена?
— Пиклер

1
Это солидный подход. Даже если соглашение о яйцах уходит, setuptools - нет, и многие все еще устанавливают deps против git-репозиториев, где яйцо
— создается

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__

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

  • путь абсолютен, но все же относителен
  • проект все еще может быть развернут в относительном контейнере

Но вам нужно следить за совместимостью платформы - Windows os.pathsep отличается от 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__переменную build in . Содержит путь к текущему файлу. Я бы реализовал getBaseOfProject в модуле в корне вашего проекта. Там я бы получил часть пути __file__и вернул бы это. Этот метод может быть использован везде в вашем проекте.


0

Я немного озадачен здесь. Хотел упаковать некоторые файлы ресурсов в файл wheel и получить к ним доступ. Выполнял упаковку, используя файл манифеста, но 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 *

Создал weel используя стандартный setup.py. Пип установил файл колеса. После установки проверил, установлены ли ресурсы. Они есть

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. Этого не будет.
— user183037
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.