如何在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__)返回用于调用脚本的path,可能是相对的,甚至是空白的(如果脚本在cwd中)。 另外,当脚本在IDLE或PythonWin中运行时,__file__不存在。

  • sys.path[0]os.path.abspath(os.path.dirname(sys.argv[0]))似乎返回脚本目录。 我不确定这两者之间是否有区别。

编辑:

我只是意识到,我想要做的将被更好地描述为“在与包含模块相同的目录中打开文件”。 换句话说,如果我导入一个模块,我写在另一个目录中,那个模块打开一个文件,我希望它在模块的目录中查找文件。 我不认为我find的任何东西都能做到

我总是使用:

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

join()调用预先指定当前的工作目录,但是文档中说如果某个path是绝对path,则所有其他path都将被删除。 因此,当dirname(__file__)返回绝对path时, getcwd()被删除。

此外, realpath调用会parsing符号链接(如果有的话)。 这可以避免在Linux系统上使用setuptools进行部署时的麻烦(脚本与/usr/bin/至less在Debian上是相符的)。

您可以使用以下方式在相同的文件夹中打开文件:

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

我使用它在Windows和Linux上捆绑多个Django应用程序的资源,它的作用就像一个魅力!

引用Python文档:

在程序启动时初始化,这个列表中的第一个项目path [0]是包含用于调用Python解释器的脚本的目录。 如果脚本目录不可用(例如,如果解释器是交互式调用的,或者脚本是从标准input中读取的),则path [0]是空string,它指示Python首先search当前目录中的模块。 请注意,由于PYTHONPATH而插入的条目之前插入了脚本目录。

sys.path [0]是你在找什么。

好的,这是我做的

sys.argv总是input到terminal中,或者在使用python.exe或pythonw.exe执行时用作文件path

例如,你可以用几种方式运行文件text.py,它们每个都给你一个不同的答案,他们总是给你pythoninput的path。

  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\ 

它会一直输出这个,不pipe你inputpython 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]给你你从解释器总是运行的文件。 根据您的需求,您需要select最适合您的需求的产品。

我会这样做:

 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构build了文件的绝对path,相当于使用normpath(join(os.getcwd(), path)) [来自pydocs]。 然后检查文件是否真的存在 ,然后使用上下文pipe理器打开它,所以你不必记得在文件句柄上调用close。 恕我直言,这样做会为您节省很多痛苦,从长远来看。