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]))スクリプトのディレクトリを返すように見えます。この2つに違いがあるかどうかはわかりません。

編集:

私がやりたいことは、「含まれているモジュールと同じディレクトリにあるファイルを開く」と表現するほうがよいことに気づきました。つまり、別のディレクトリに書き込んだモジュールをインポートし、そのモジュールがファイルを開いた場合、モジュールのディレクトリでファイルを探します。私が見つけたものはそれを行うことができるとは思いません...

回答:


199

私は常に使用します:

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

join()コールは、現在の作業ディレクトリを付加ますが、ドキュメントは、いくつかのパスが絶対的であれば、それを左に他のすべてのパスがドロップされることを言います。したがって、絶対パスを返すgetcwd()とドロップされdirname(__file__)ます。

また、realpathシンボリックリンクが見つかった場合は、呼び出しによって解決されます。これにより、Linuxシステムでsetuptoolsを使用して展開するときの問題が回避されます(スクリプトは/usr/bin/、少なくともDebianではシンボリックリンクされています)。

以下を使用して、同じフォルダー内のファイルを開くことができます。

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

これを使用して、WindowsとLinuxの両方で複数のDjangoアプリケーションにリソースをバンドルします。


4
__file__使用できない場合は、のsys.argv[0]代わりに使用してくださいdirname(__file__)。残りは期待どおりに動作するはずです。__file__ライブラリコードでsys.argv[0]は、特にサードパーティのスクリプトを介してインポートした場合、コードをまったく指さない可能性があるため、使用するのが好きです。
アンドレ・キャノン

1
これに関する問題は、実行中のファイルがインタラプタから直接であるか、インポートされているかによって異なることです。fileとsys.argv [0] の違いについては私の回答を参照してください
Zimm3r

それで、Zimm3rの回答で説明されているバリエーションは、realpath( join( getcwd(), dirname(__file__) ))ここで説明されているように使用することで対処されると言うのは正しいですか?
pianoJames

44

Pythonのドキュメントから引用するには:

プログラムの起動時に初期化されると、このリストの最初の項目であるpath [0]は、Pythonインタープリターの呼び出しに使用されたスクリプトを含むディレクトリです。スクリプトディレクトリが利用できない場合(たとえば、インタプリタがインタラクティブに呼び出される場合、またはスクリプトが標準入力から読み取られる場合)、path [0]は空の文字列で、Pythonが最初に現在のディレクトリでモジュールを検索するように指示します。PYTHONPATHの結果として挿入されたエントリの前に、スクリプトディレクトリが挿入されていることに注意してください。

sys.path [0]は、探しているものです。


10
そして、ファイルのフルパスのため:os.path.join(sys.path[0], 'some file.txt')。これは、すべてのシステムでスペースとスラッシュを正しく処理するはずです。
Jacktose

EDITの後ではなく、最初の質問に対するこの回答。
mcoolive 2017年

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:\ Documents and Settings \ Admin \ test.py"と入力しても、常にこれが出力されます

__file__の使用 に関する問題これら2つのファイル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]は常にインタープリターから実行したファイルを提供します。ニーズに応じて、ニーズに最適なものを選択する必要があります。


3
これは、実装がドキュメントを反映しているという精巧な証明です。__file__されるはずの「常に現在のファイルへのあなたの道を与える」、とsys.argv[0]されるはず「常にプロセスを開始したスクリプトのパスを与える」へ。いずれにしても、__file__呼び出されるスクリプトで使用すると、常に正確な結果が得られます。
アンドレ・キャノン

__file__スクリプトの最上位に参照がある場合、期待どおりに動作します。
マシュー・シンケル


-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から] を使用することと同等です。次に、そのファイルが実際に存在するかどうかを確認し、コンテキストマネージャーを使用してファイルを開くため、ファイルハンドルに対してcloseを呼び出すことを覚えておく必要はありません。私見、このようにすることで、長期的には多くの苦痛を省くことができます。


これは投稿者の質問には答えません。dln385 os.path.abspathは、スクリプトが現在のディレクトリにない場合、スクリプトと同じフォルダー内のファイルへのパスを解決しないと具体的に述べています。
アンドレ・キャノン

ああ!私は、ユーザーが、彼らが読みたかったファイルと同じディレクトリにこのスクリプトを実行していたと仮定しないで自分のPYTHONPATHで何かのモジュールディレクトリに。それは私が仮定をすることを教えてくれます...
dcolish

Pythonランタイムがこのような関数を使用してOSファイルシステムを検索することは不可能であるため、abspathは機能しません。
akshat thakar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.