如何在与Python脚本相同的目录中可靠地打开文件

How to reliably open a file in the same directory as a Python script

我以前只需使用如下命令就可以打开与当前运行的python脚本位于同一目录中的文件

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

但是,我发现当在Windows中双击脚本时,它会试图从错误的目录打开文件。

从那以后我就用了一个命令

1
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中)。另外,当脚本在空闲或pythonwin中运行时,__file__不存在。

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

编辑:

我刚刚意识到我想要做的是更好地描述为"在与包含模块相同的目录中打开一个文件"。换句话说,如果我导入一个写在另一个目录中的模块,并且该模块打开一个文件,我希望它在模块的目录中查找该文件。我认为我所发现的一切都无法做到…


我总是使用:

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

join()调用预先准备了当前的工作目录,但是文档中说,如果某个路径是绝对路径,那么它剩下的所有其他路径都将被删除。因此,当dirname(__file__)返回绝对路径时,getcwd()被丢弃。

另外,如果找到符号链接,realpath调用将解析符号链接。这可以避免在Linux系统上使用安装工具部署时出现问题(脚本与/usr/bin/符号链接,至少在debian上是这样)。

可以使用以下方法打开同一文件夹中的文件:

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

我使用它将资源与Windows和Linux上的几个Django应用程序捆绑在一起,它的工作方式很有魅力!


从python文档中引用:

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.

sys.path[0]是您要查找的。


好的,这是我的工作

使用python.exe或pythonw.exe执行时,sys.argv始终是您在终端中键入或用作文件路径的内容。

例如,您可以通过多种方式运行文件text.py,每种方式都为您提供不同的答案,它们总是为您提供键入python的路径。

1
2
3
4
    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。

1
2
    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

这将输出:

1
   C:\Documents and Settings\Admin\

无论您键入python test.py还是python"c:documents and settingsadmin est.py",它都将始终输出此信息。

使用文件的问题__考虑这两个文件测试.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()

导入测试.py

1
2
3
4
5
6
7
8
import test
import sys

test.paths()

print"--------"
print __file__
print sys.argv[0]

输出"python test.py"

1
2
3
4
5
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"

1
2
3
4
5
6
7
8
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]总是为您提供从解释器运行的文件。根据你的需要,你需要选择最适合你需要的。


我会这样做:

1
2
3
4
5
6
7
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。imho,这样做可以从长远来看减轻你的痛苦。