2010-10-30 4 views
90

я использовал, чтобы открыть файлы, которые были в том же каталоге, что и в настоящее время работает скрипт на Python, просто используя командуКак надежно открыть файл в том же каталоге, что и сценарий 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:

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

ответ

108

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

__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

Если '__file__' не может быть использован, используйте' sys.argv [0] 'вместо' dirname (__ file __) '. Остальное должно работать так, как ожидалось. Мне нравится использовать '__file__', потому что в библиотечном коде' sys.argv [0] 'может не указывать на ваш код вообще, особенно если он импортируется через какой-то сторонний скрипт. –

+1

Проблема с этим заключается в том, что он будет меняться, если вы запускаете файл из прерывателя напрямую или если он импортируется. См. Мой ответ за различия между __file__ и sys.argv [0] – Zimm3r

+0

. Правильно ли говорить, что вариант, описанный в ответе Zimm3r, адресуется с помощью 'realpath (join (getcwd(), dirname (__ file__)))' as описанный здесь? – pianoJames

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

+0

Это не отвечает на вопрос плаката. dln385 специально сказал, что 'os.path.abspath' не разрешает пути к файлам в той же папке, что и скрипт, если скрипт не находится в текущем каталоге. –

+0

AH! Я предположил, что пользователь запускает этот скрипт в том же каталоге, что и файл, который они хотели прочитать, * NOT * в модуле dir чего-то в PYTHONPATH. Это научит меня делать предположения ... – dcolish

12

Ok вот что я

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\ 

он будет всегда выводится это не имеет значения, если вы наберете питона test.py или питона "C: \ Documents и 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] 

Выход из "питона 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 

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

+1

Это подробное доказательство того, что реализация отражает документацию. '__file__' является * предположительным * для« всегда дает вам путь к текущему файлу », а' sys.argv [0] 'is * предполагается *, чтобы« всегда указывать путь к скрипту, который инициировал этот процесс ». В любом случае использование '__file__' в скрипте, который вызывается, всегда дает вам точные результаты. –

+0

Я просто указывал на это и давал что-то, чтобы показать это. – Zimm3r

+0

Если у вас есть ссылка на '__file__' на верхнем уровне скрипта, он будет работать так, как ожидалось. –

18

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

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

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

+1

И для полного пути к файлу: 'os.path.join (sys.path [0], 'some file.txt')'. Это должно корректно обрабатывать пробелы и косые черты во всех системах. – P1h3r1e3d13

+0

Этот ответ на первый вопрос, а не тот, который был после EDIT. – mcoolive

Смежные вопросы