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


157

Я використовував для відкриття файлів, що знаходились у тому самому каталозі, що і поточний сценарій Python, просто використовуючи таку команду, як

open("Some file.txt", "r")

Однак я виявив, що коли сценарій запускався в Windows, двічі клацнувши його, він спробує відкрити файл із неправильної каталоги.

З того часу я використовував команду форми

open(os.path.join(sys.path[0], "Some file.txt"), "r")

всякий раз, коли я хотів відкрити файл. Це працює для мого конкретного використання, але я не впевнений, якщо це sys.path[0]може вийти з ладу в іншому випадку використання.

Отже, моє запитання: який найкращий і найнадійніший спосіб відкрити файл, який знаходиться в тому ж каталозі, що і поточний сценарій Python?

Ось що я міг розібратися досі:

  • os.getcwd()і os.path.abspath('')повернути "поточний робочий каталог", а не каталог сценаріїв.

  • os.path.dirname(sys.argv[0])і os.path.dirname(__file__)повернути шлях, використовуваний для виклику сценарію, який може бути відносним або навіть порожнім (якщо сценарій знаходиться у cwd). Також __file__не існує, коли сценарій запускається в IDLE або PythonWin.

  • sys.path[0]і, os.path.abspath(os.path.dirname(sys.argv[0]))здається, повертає каталог сценаріїв. Я не впевнений, чи є різниця між цими двома.

Редагувати:

Я просто зрозумів, що те, що я хочу зробити, було б краще описати як "відкрити файл у тій самій каталозі, що і модуль, що містить". Іншими словами, якщо я імпортую модуль, який я написав, що це в іншій каталозі, і цей модуль відкриває файл, я хочу, щоб він шукав файл у каталозі модуля. Я не думаю, що все, що я знайшов, здатне це зробити ...

Відповіді:


199

Я завжди використовую:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__)))

join()Виклик передує поточного робочого каталогу, але в документації сказано , що якщо якийсь - то шлях є абсолютним, всі інші шляхи зліва від нього відкидаються. Тому getcwd()випадає, коли dirname(__file__)повертає абсолютний шлях.

Також realpathдзвінок вирішує символічні посилання, якщо такі знайдені. Це дозволяє уникнути неприємностей під час розгортання з setuptools в системах Linux (сценарії посилаються на /usr/bin/- принаймні на Debian).

Ви можете використовувати наступне для відкриття файлів у тій самій папці:

f = open(os.path.join(__location__, 'bundled-resource.jpg'));
# ...

Я використовую це для об'єднання ресурсів з кількома програмами Django для Windows і Linux, і це працює як шарм!


4
Якщо __file__не можна використовувати, використовуйте sys.argv[0]замість dirname(__file__). Решта повинні працювати як очікувалося. Мені подобається використовувати, __file__оскільки код бібліотеки sys.argv[0]може взагалі не вказувати на ваш код, особливо якщо він імпортований через якийсь сторонній сценарій.
Андре Карон

1
Проблема в цьому полягає в тому, що вона буде відрізнятися, якщо ви запущений файл перебуваєте безпосередньо від переривача або якщо він імпортується. Дивіться мою відповідь щодо відмінностей між файлом та sys.argv [0]
Zimm3r

Тож чи правильно сказати, що варіацію, описану у відповіді Zimm3r, вирішують, використовуючи realpath( join( getcwd(), dirname(__file__) ))описане тут?
pianoJames

44

Цитувати з документації Python:

Як ініціалізовано при запуску програми, перший пункт цього списку, шлях [0], - це каталог, що містить сценарій, який використовувався для виклику інтерпретатора Python. Якщо каталог сценаріїв недоступний (наприклад, якщо інтерпретатор викликається інтерактивно, або якщо сценарій зчитується зі стандартного вводу), шлях [0] - це порожня рядок, яка спочатку спрямовує Python на пошук модулів у поточному каталозі. Зауважте, що каталог сценаріїв вставляється перед записами, вставленими в результаті PYTHONPATH.

sys.path [0] - це те, що ви шукаєте.


10
І для повного шляху файлу: os.path.join(sys.path[0], 'some file.txt'). Це повинно правильно обробляти пробіли та косої риски у всіх системах.
Джектоз

Ця відповідь на перше запитання, а не відповідь після EDIT.
mcoolive

22

Ок, ось що я роблю

sys.argv - це завжди те, що ви вводите в термінал або використовуєте як шлях до файлу, виконуючи його за допомогою python.exe або pythonw.exe

Наприклад, ви можете запустити файл text.py декількома способами, кожен з них дає вам іншу відповідь, вони завжди дають вам шлях, який був введений python.

    C:\Documents and Settings\Admin>python test.py
    sys.argv[0]: test.py
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py"
    sys.argv[0]: C:\Documents and Settings\Admin\test.py

Добре, знайте, ви можете отримати ім'я файлу, велика справа, тепер, щоб отримати каталог додатків, ви можете знати, як використовувати os.path, зокрема abspath та dirname

    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

Це виведе це:

   C:\Documents and Settings\Admin\

це завжди буде виводити це незалежно від того, якщо ви введете python test.py або python "C: \ Документи та налаштування \ Адміністратор \ test.py"

Проблема з використанням __file__ Розгляньте ці два файли test.py

import sys
import os

def paths():
        print "__file__: %s" % __file__
        print "sys.argv: %s" % sys.argv[0]

        a_f = os.path.abspath(__file__)
        a_s = os.path.abspath(sys.argv[0])

        print "abs __file__: %s" % a_f
        print "abs sys.argv: %s" % a_s

if __name__ == "__main__":
    paths()

import_test.py

import test
import sys

test.paths()

print "--------"
print __file__
print sys.argv[0]

Вихід "python test.py"

C:\Documents and Settings\Admin>python test.py
__file__: test.py
sys.argv: test.py
abs __file__: C:\Documents and Settings\Admin\test.py
abs sys.argv: C:\Documents and Settings\Admin\test.py

Вихід "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py
__file__: C:\Documents and Settings\Admin\test.pyc
sys.argv: test_import.py
abs __file__: C:\Documents and Settings\Admin\test.pyc
abs sys.argv: C:\Documents and Settings\Admin\test_import.py
--------
test_import.py
test_import.py

Отже, як ви бачите, файл завжди дає файл python, з якого він запускається, де sys.argv [0] дає вам файл, який ви завжди запускали від інтерпретатора. Залежно від ваших потреб, вам потрібно буде вибрати, який саме відповідає вашим потребам.


3
Це детальний доказ того, що реалізація відображає документацію. __file__як передбачається , щоб «завжди дасть вам шлях до поточного файлу», і sys.argv[0]як передбачається , щоб «завжди дають шлях до сценарію , який ініціював процес». У будь-якому випадку використання __file__в скрипті, який викликається, завжди дає точні результати.
Андре Карон

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

-1

Я зміг успішно використовувати код, наданий dcolish, оскільки у мене виникли подібні проблеми з читанням певного текстового файлу. Файл не є тим самим диском, що і файл Python.


1
Будь ласка, не додайте у відповідь "спасибі". Коли у вас буде достатня репутація , ви зможете проголосувати питання та відповіді, які вам здаються корисними. - З огляду
Роберто Кабоні

-3

Я зробив би це так:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

Вищевказаний код будує абсолютний шлях до файлу за допомогою abspath і еквівалентно використанню normpath(join(os.getcwd(), path))[це від pydocs]. Потім він перевіряє, чи існує цей файл , а потім використовує диспетчер контекстів, щоб відкрити його, так що вам не потрібно пам'ятати, щоб викликати закриття на ручці файлу. ІМХО, зробивши це таким чином, ви позбавите багато болю в довгостроковій перспективі.


Це не відповідає на запитання афіші. dln385 спеціально сказав, що os.path.abspathне вирішує шляхи до файлів у тій самій папці, що і сценарій, якщо сценарій не знаходиться в поточному каталозі.
Андре Карон

А-а! Я припускав, що користувач виконує цей скрипт у тому ж режимі, що і файл, який вони хотіли прочитати, а не в модулі dir чогось у своєму PYTHONPATH. Це навчить мене робити припущення ...
dcolish

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