Відносні шляхи в Python


245

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


Відповіді:


325

У файлі, у якому є сценарій, потрібно зробити щось подібне:

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__розширеннями на C. Наприклад, я можу це зробити на своєму 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
Чи правильно я вважаю, що файл доступний не завжди (наприклад, коли ви запускаєте файл безпосередньо, а не імпортуєте його)?
Стівен Едмондс

@Stephen Edmonds Я використовую його файл, який я запускаю, а не імпортувати, і він чудово працює.
бодотака

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('..'))

2
os.path.dirname(__file__)дав мені порожню рядок. Це спрацювало чудово.
Darragh Enright

3
Це здається, що дає батьківський каталог каталогу, з якого запущений скрипт, а не з розташування сценарію.
Кокелікот

10
os.path.realpath('..')дає вам батьківський каталог поточного робочого режиму . Зазвичай це не те, що ти хочеш.
Martijn Pieters

1
@DarraghEnright: Це відбувається лише в середовищі упаковки Python-script-to-exe. Це одне з рідкісних винятків, коли покладатися на поточний робочий директор буде альтернативою.
Martijn Pieters

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')

Прийнята відповідь може вводити в оману в деяких випадках. Для детальної інформації зверніться до цього посилання


4
Так, використання os.path.joinкраще, оскільки воно з'єднує їх із специфічним для ОС роздільником.
Фаршид Т

'/relative/path...'не є відносним шляхом. Це навмисно?
steveire

Ця відповідь застаріла, оскільки головна відповідь була відредагована для використання належного відносного шляху в os.path.join(). Залишається перевага використовувати окремі рядки для кожного елемента шляху через жорстке кодування роздільника шляху.
Martijn Pieters

@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)

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] Немає такого файлу чи каталогу: '<path>', де <path> має правильні сегменти шляху, але використовує \\ для роздільників.
lonstar

11

Див. Sys.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]встановлена на що - то інше в цілому.
Martijn Pieters

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__.
Martijn Pieters

4

Привіт, перш за все, ви повинні зрозуміти функції os.path.abspath (шлях) та os.path.relpath (шлях)

Якщо коротко, os.path.abspath (шлях) робить відносний шлях до абсолютного шляху . І якщо наданий шлях сам по собі є абсолютним шляхом, то функція повертає той самий шлях.

аналогічно os.path.relpath (шлях) робить абсолютний шлях до відносного шляху . І якщо наданий шлях сам по собі відносний шлях, то функція повертає той самий шлях.

Нижче на прикладі можна зрозуміти вищезазначене поняття :

припустимо, у мене є файл input_file_list.txt, який містить список вхідних файлів, які обробляються моїм сценарієм python.

D: \ conc \ input1.dic

D: \ conc \ input2.dic

D: \ Copyioconc \ input_file_list.txt

Якщо ви бачите вище структуру папок, input_file_list.txt присутній у папці Copyofconc, а файли, які обробляються сценарієм python, містяться у папці conc

Але вміст файлу input_file_list.txt є таким, як показано нижче:

.. \ conc \ input1.dic

.. \ conc \ input2.dic

І мій скрипт python присутній у D: drive.

І відносний шлях, наданий у файлі 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__))

Це буде працювати навіть в модулі.


Замість повторного імпорту ви б використовували sys.modules['__main__'].__file__.
Martijn Pieters

3

Альтернатива, яка працює для мене:

this_dir = os.path.dirname(__file__) 
filename = os.path.realpath("{0}/relative/file.path".format(this_dir))

0

Те, що для мене працювало, використовує sys.path.insert. Тоді я вказав каталог, який мені потрібно було пройти. Наприклад, мені просто потрібно було підійти до одного каталогу.

import sys
sys.path.insert(0, '../')

1
Це спирається на поточний робочий каталог, який може кардинально відрізнятися від того, що ви насправді хочете.
Martijn Pieters

-2

Я не впевнений, чи це стосується деяких старих версій, але я вважаю, що в Python 3.3 є вбудована відносна підтримка.

Наприклад, наступний код повинен створити текстовий файл у тій же папці, що і сценарій python:

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

(зауважте, що на початку не повинно бути прямолінійної чи зворотної нахилів, якщо це відносний шлях)


вірно, тож це буде працювати з КВД, а це не те, про що вимагає ОП. Хочете працювати з сценаріїв розташування.
Самі Бенчеріф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.