Относительные пути в Python

246

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

baudtack
источник

Ответы:

326

В файле со сценарием вы хотите сделать что-то вроде этого:

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

Это даст вам абсолютный путь к файлу, который вы ищете. Обратите внимание, что если вы используете setuptools, вам, вероятно, следует вместо этого использовать его API ресурсов пакета .

ОБНОВЛЕНИЕ : я отвечаю на комментарий здесь, чтобы я мог вставить пример кода. :-)

Правильно ли я считаю, что __file__это не всегда доступно (например, когда вы запускаете файл напрямую, а не импортируете его)?

Я предполагаю, что вы имеете в виду __main__сценарий, когда упоминаете о запуске файла напрямую. Если так, то в моей системе это не так (python 2.5.1 в OS X 10.5.7):

#foo.py
import os
print os.getcwd()
print __file__

#in the interactive interpreter
>>> import foo
/Users/jason
foo.py

#and finally, at the shell:
~ % python foo.py
/Users/jason
foo.py

Тем не менее, я знаю, что есть некоторые причуды с __file__расширениями Си. Например, я могу сделать это на моем Mac:

>>> import collections #note that collections is a C extension in Python 2.5
>>> collections.__file__
'/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-
dynload/collections.so'

Тем не менее, это вызывает исключение на моем компьютере с Windows.

Джейсон Бейкер
источник
1
Правильно ли я считаю, что файл не всегда доступен (например, когда вы запускаете файл напрямую, а не импортируете его)?
Стивен Эдмондс
@ Стефан Эдмондс Я использую файл, который я запускаю, а не импортирую, и он прекрасно работает.
Бодэкт
22
Обратите внимание, что вы должны везде использовать os.path.join для переносимости:filename = os.path.join(dir, 'relative', 'path', 'to', 'file', 'you' , 'want')
ford
22
os.path.dirname(__file__)можете дать пустую строку, используйте os.path.dirname(os.path.abspath(__file__))вместо
Дмитрий Трофимов
14
Это второстепенная вещь, но ПОЖАЛУЙСТА, не используйте dir в качестве имени переменной, поскольку он является встроенным.
Дэвид
63

вам нужно os.path.realpath(пример ниже добавляет родительский каталог к ​​вашему пути)

import sys,os
sys.path.append(os.path.realpath('..'))
user989762
источник
2
os.path.dirname(__file__)дал мне пустую строку. Это сработало отлично.
Дарра Энрайт
3
Это, кажется, дает родителю каталога, из которого запускается скрипт, а не местоположение скрипта.
Coquelicot
10
os.path.realpath('..')дает вам родительский каталог текущего рабочего каталога . Обычно это не то, что вы хотите.
Мартин Питерс
1
@DarraghEnright: Это происходит только в среде упаковки Python-script-to-exe. Это одно из редких исключений, когда в качестве альтернативы можно полагаться на текущий рабочий каталог.
Мартин Питерс
52

Как указано в принятом ответе

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, '/relative/path/to/file/you/want')

Я просто хочу добавить, что

последняя строка не может начинаться с обратной косой черты, в действительности ни одна строка не должна включать обратную косую черту

Это должно быть что-то вроде

import os
dir = os.path.dirname(__file__)
filename = os.path.join(dir, 'relative','path','to','file','you','want')

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

Ahmed
источник
4
Да, os.path.joinлучше использовать, потому что он объединяет их с разделителем для конкретной ОС.
Фаршид Т
'/relative/path...'это не относительный путь. Это намеренно?
Steveire
Этот ответ устарел, так как верхний ответ был отредактирован для использования правильного относительного пути в os.path.join(). Осталось только использовать отдельные строки для каждого элемента пути, а не жестко кодировать разделитель пути.
Мартин Питерс
@MartijnPieters Да, верхний ответ был отредактирован, чтобы соответствовать этому частично, но отдельные строки не являются предпочтением - разделение таких строк делает его независимым от ОС.
jshrimp29
26

Сейчас 2018 год, и Python уже давно эволюционировал __future__. Так как об использовании удивительного pathlibприходит с Python 3.4 , чтобы выполнить задачу , вместо того , чтобы бороться с os, os.path, glob, shutilи т.д.

Итак, у нас есть 3 пути (возможно, дублированные):

  • mod_path: это путь простого вспомогательного скрипта
  • src_path: который содержит пару файлов шаблонов, ожидающих копирования.
  • cwd: текущий каталог , место назначения этих файлов шаблонов.

и проблема в том, что: у нас нет полного пути src_path, мы только знаем, что это относительный путь к mod_path.

Теперь давайте решим это с помощью удивительного pathlib:

# Hope you don't be imprisoned by legacy Python code :)
from pathlib import Path

# `cwd`: current directory is straightforward
cwd = Path.cwd()

# `mod_path`: According to the accepted answer and combine with future power
# if we are in the `helper_script.py`
mod_path = Path(__file__).parent
# OR if we are `import helper_script`
mod_path = Path(helper_script.__file__).parent

# `src_path`: with the future power, it's just so straightforward
relative_path_1 = 'same/parent/with/helper/script/'
relative_path_2 = '../../or/any/level/up/'
src_path_1 = (mod_path / relative_path_1).resolve()
src_path_2 = (mod_path / relative_path_2).resolve()

В будущем все просто. : D


Кроме того, мы можем выбрать и проверить и скопировать / переместить эти файлы шаблона с помощью pathlib:

if src_path != cwd:
    # When we have different types of files in the `src_path`
    for template_path in src_path.glob('*.ini'):
        fname = template_path.name
        target = cwd / fname
        if not target.exists():
            # This is the COPY action
            with target.open(mode='wb') as fd:
                fd.write(template_path.read_bytes())
            # If we want MOVE action, we could use:
            # template_path.replace(target)
YaOzI
источник
14

Рассмотрим мой код:

import os


def readFile(filename):
    filehandle = open(filename)
    print filehandle.read()
    filehandle.close()



fileDir = os.path.dirname(os.path.realpath('__file__'))
print fileDir

#For accessing the file in the same folder
filename = "same.txt"
readFile(filename)

#For accessing the file in a folder contained in the current folder
filename = os.path.join(fileDir, 'Folder1.1/same.txt')
readFile(filename)

#For accessing the file in the parent folder of the current folder
filename = os.path.join(fileDir, '../same.txt')
readFile(filename)

#For accessing the file inside a sibling folder.
filename = os.path.join(fileDir, '../Folder2/same.txt')
filename = os.path.abspath(os.path.realpath(filename))
print filename
readFile(filename)
Фахад Халим
источник
Когда я запускаю это в Windows, я получаю сообщение об ошибке: FileNotFoundError: [Errno 2] Нет такого файла или каталога: '<путь>', где <путь> имеет правильные сегменты пути, но использует \\ для разделителей.
Lonstar
11

Смотрите sys.path Инициализированный при запуске программы, первый элемент этого списка, path [0], является каталогом, содержащим скрипт, который использовался для вызова интерпретатора Python.

Используйте этот путь в качестве корневой папки, из которой вы применяете свой относительный путь

>>> import sys
>>> import os.path
>>> sys.path[0]
'C:\\Python25\\Lib\\idlelib'
>>> os.path.relpath(sys.path[0], "path_to_libs") # if you have python 2.6
>>> os.path.join(sys.path[0], "path_to_libs")
'C:\\Python25\\Lib\\idlelib\\path_to_libs'
Том Лейс
источник
3
Это не обязательно правда. Обычно sys.path [0] является пустой строкой или точкой, которая является относительным путем к текущему каталогу. Если вы хотите текущий каталог, используйте os.getcwd.
Джейсон Бейкер
В первоначальном постере отмечалось, что текущий рабочий каталог - это неправильное место для определения относительного пути. Вы правы, говоря, что sys.path [0] не всегда допустим.
Том Лейс
Нет, sys.path[0]не всегда установлен родительский каталог. Python код может быть вызван с -cили -mили через встроенный интерпретатор, в котором точка sys.path[0]установлена на что - то другое в целом.
Мартин Питерс
6

Вместо того, чтобы использовать

import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

как и в принятом ответе, было бы более надежно использовать:

import inspect
import os
dirname = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
filename = os.path.join(dirname, 'relative/path/to/file/you/want')

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

Эти ответы дают более подробную информацию: https://stackoverflow.com/a/31867043/5542253 и https://stackoverflow.com/a/50502/5542253

смайлик
источник
5
inspect.stack()это дорогая функция для вызова. Он извлекает информацию для всех кадров стека, которые вы затем отбрасываете и получаете только верхний. Это в основном вызывает inspect.getfile()объект модуля, который просто возвращает module.__file__. Вы гораздо лучше просто использовать __file__.
Мартин Питерс
4

Привет, прежде всего вы должны понимать функции os.path.abspath (путь) и os.path.relpath (путь)

Короче говоря os.path.abspath (путь) делает относительный путь к абсолютному пути . И если указанный путь сам по себе является абсолютным, функция возвращает тот же путь.

аналогично os.path.relpath (путь) делает абсолютный путь к относительному пути . И если указанный путь является относительным, то функция возвращает тот же путь.

Приведенный ниже пример может помочь вам правильно понять вышеуказанную концепцию :

Предположим, у меня есть файл input_file_list.txt, который содержит список входных файлов для обработки моим скриптом Python.

D: \ конц \ input1.dic

D: \ конц \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Если вы видите вышеупомянутую структуру папок, input_file_list.txt присутствует в папке Copyofconc, а файлы, обрабатываемые сценарием python, присутствуют в папке conc.

Но содержимое файла input_file_list.txt показано ниже:

.. \ конц \ input1.dic

.. \ конц \ input2.dic

А мой скрипт на python присутствует на диске D : .

И относительный путь, указанный в файле input_file_list.txt, относится к пути файла input_file_list.txt .

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

Поскольку мой относительный путь относительно input_file_list.txt , то есть «D: \ Copyofconc» , я должен изменить текущий рабочий каталог на «D: \ Copyofconc» .

Поэтому я должен использовать os.chdir ('D: \ Copyofconc') , поэтому текущий рабочий каталог должен быть "D: \ Copyofconc" .

Теперь, чтобы получить файлы input1.dic и input2.dic , я прочитаю строки ".. \ conc \ input1.dic", затем воспользуюсь командой

input1_path = os.path.abspath ('.. \ conc \ input1.dic') (чтобы изменить относительный путь на абсолютный путь. Здесь текущим рабочим каталогом является "D: \ Copyofconc", файл ". \ conc \ input1. dic "должен быть доступен относительно" D: \ Copyofconc ")

поэтому input1_path должен быть "D: \ conc \ input1.dic"

Чандраджиоти Дас
источник
4

Этот код вернет абсолютный путь к основному сценарию.

import os
def whereAmI():
    return os.path.dirname(os.path.realpath(__import__("__main__").__file__))

Это будет работать даже в модуле.

BookOwl
источник
Вместо повторного импорта вы бы использовали sys.modules['__main__'].__file__.
Мартин Питерс
3

Альтернатива, которая работает для меня:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))
J0hnG4lt
источник
0

То, что работает для меня, использует sys.path.insert. Затем я указал каталог, который мне нужен. Например, мне просто нужно было перейти на один каталог.

import sys
sys.path.insert(0, '../')
Белый кот
источник
1
Это зависит от текущего рабочего каталога, который может радикально отличаться от того, что вы на самом деле хотите.
Мартин Питерс
-2

Я не уверен, относится ли это к некоторым более старым версиям, но я считаю, что Python 3.3 имеет встроенную поддержку относительного пути.

Например, следующий код должен создать текстовый файл в той же папке, что и скрипт python:

open("text_file_name.txt", "w+t")

(обратите внимание, что в начале не должно быть прямой или обратной косой черты, если это относительный путь)

Сами Бенчериф
источник
правильно, так что это будет работать от CWD, что не то, что просит OP. Желание работать из местоположения скриптов.
Сами Бенчериф