Как надежно открыть файл в том же каталоге, что и скрипт Python - PullRequest
131 голосов
/ 30 октября 2010

Раньше я открывал файлы, которые находились в той же директории, что и текущий скрипт 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])), похоже, возвращают каталог скриптов. Я не уверен, есть ли разница между этими двумя.

Edit:

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

Ответы [ 4 ]

158 голосов
/ 30 октября 2010

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

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

32 голосов
/ 29 марта 2011

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

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

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

18 голосов
/ 31 октября 2010

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

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

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

0 голосов
/ 30 октября 2010

Я бы сделал это так:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...