Відносні шляхи в 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.