Как надежно открыть файл в том же каталоге, что и Python script

Я использовал для открытия файлов, которые были в том же каталоге, что и текущий Python script, просто используя команду типа

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

Однако я обнаружил, что когда script был запущен в Windows, дважды щелкнув его, он попытается открыть файл из неправильного каталога.

С тех пор я использовал команду формы

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

всякий раз, когда я хотел открыть файл. Это работает для моего конкретного использования, но я не уверен, что sys.path[0] может потерпеть неудачу в некоторых других случаях использования.

Итак, мой вопрос: какой лучший и надежный способ открыть файл, который находится в том же каталоге, что и текущий Python script?

Вот что мне удалось выяснить до сих пор:

  • os.getcwd() и os.path.abspath('') верните "текущий рабочий каталог", а не каталог script.

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

  • sys.path[0] и os.path.abspath(os.path.dirname(sys.argv[0])), похоже, возвращают каталог script. Я не уверен, есть ли разница между этими двумя.

Edit:

Я просто понял, что то, что я хочу сделать, лучше описать как "открыть файл в том же каталоге, что и содержащий модуль". Другими словами, если я импортирую модуль, я написал, что в другом каталоге, и этот модуль открывает файл, я хочу, чтобы он искал файл в каталоге модуля. Я не думаю, что все, что я нашел, может это сделать...

Ответ 1

Я всегда использую:

__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, и это работает как шарм!

Ответ 2

Для цитирования из документации Python:

Как инициализировано при запуске программы, первым элементом этого списка является путь [0], это каталог, содержащий script, который использовался для вызова интерпретатора Python. Если каталог script недоступен (например, если интерпретатор вызывается интерактивно или если script считывается со стандартного ввода), путь [0] - это пустая строка, которая направляет Python на поиск модулей в текущем каталоге сначала, Обратите внимание, что каталог script вставлен перед вставками в результате PYTHONPATH.

sys.path [0] - это то, что вы ищете.

Ответ 3

Хорошо, вот что я делаю

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:\Documents and Settings\Admin\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] предоставляет вам файл, который вы всегда запускали из интерпретатора. В зависимости от ваших потребностей вам нужно будет выбрать, какой из них лучше всего подходит вашим потребностям.

Ответ 4

Я бы сделал это следующим образом:

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], Затем он проверяет, действительно ли этот файл exists, а затем использует диспетчер контекста, чтобы открыть его, чтобы вам не приходилось задерживать вызов дескриптор файла. ИМХО, делая это таким образом, в долгосрочной перспективе сэкономит вам много боли.