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

问题:如何可靠地在与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]))似乎返回脚本目录。我不确定这两者之间是否有任何区别。

编辑:

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

I used to open files that were in the same directory as the currently running Python script by simply using a command like

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

However, I discovered that when the script was run in Windows by double-clicking it, it would try to open the file from the wrong directory.

Since then I’ve used a command of the form

open(os.path.join(sys.path[0], "Some file.txt"), "r")

whenever I wanted to open a file. This works for my particular usage, but I’m not sure if sys.path[0] might fail in some other use case.

So my question is: What is the best and most reliable way to open a file that’s in the same directory as the currently running Python script?

Here’s what I’ve been able to figure out so far:

  • os.getcwd() and os.path.abspath('') return the “current working directory”, not the script directory.

  • os.path.dirname(sys.argv[0]) and os.path.dirname(__file__) return the path used to call the script, which may be relative or even blank (if the script is in the cwd). Also, __file__ does not exist when the script is run in IDLE or PythonWin.

  • sys.path[0] and os.path.abspath(os.path.dirname(sys.argv[0])) seem to return the script directory. I’m not sure if there’s any difference between these two.

Edit:

I just realized that what I want to do would be better described as “open a file in the same directory as the containing module”. In other words, if I import a module I wrote that’s in another directory, and that module opens a file, I want it to look for the file in the module’s directory. I don’t think anything I’ve found is able to do that…


回答 0

我一直使用:

__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应用程序捆绑在一起,它的工作原理很吸引人!

I always use:

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

The join() call prepends the current working directory, but the documentation says that if some path is absolute, all other paths left of it are dropped. Therefore, getcwd() is dropped when dirname(__file__) returns an absolute path.

Also, the realpath call resolves symbolic links if any are found. This avoids troubles when deploying with setuptools on Linux systems (scripts are symlinked to /usr/bin/ — at least on Debian).

You may the use the following to open up files in the same folder:

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

I use this to bundle resources with several Django application on both Windows and Linux and it works like a charm!


回答 1

引用Python文档:

在程序启动时初始化后,该列表的第一项path [0]是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果交互式调用解释器或从标准输入中读取脚本),则path [0]为空字符串,该字符串将引导Python首先搜索当前目录中的模块。请注意,由于PYTHONPATH的结果,在插入条目之前插入了脚本目录。

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

To quote from the Python documentation:

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] is what you are looking for.


回答 2

好,这就是我要做的

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]始终为您提供从解释器运行的文件。根据您的需求,您将需要选择最适合您的需求。

Ok here is what I do

sys.argv is always what you type into the terminal or use as the file path when executing it with python.exe or pythonw.exe

For example you can run the file text.py several ways, they each give you a different answer they always give you the path that python was typed.

    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

Ok so know you can get the file name, great big deal, now to get the application directory you can know use os.path, specifically abspath and dirname

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

That will output this:

   C:\Documents and Settings\Admin\

it will always output this no matter if you type python test.py or python “C:\Documents and Settings\Admin\test.py”

The problem with using __file__ Consider these two files 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]

Output of “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

Output of “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

So as you can see file gives you always the python file it is being run from, where as sys.argv[0] gives you the file that you ran from the interpreter always. Depending on your needs you will need to choose which one best fits your needs.


回答 3

我在读取特定文本文件时遇到类似问题,因此能够成功使用dcolish提供的代码。该文件与Python文件不在同一cwd中。

I was able to use the code provided by dcolish successfully as I was having a similar issue with reading a specific text file. The file is not in the same cwd as the Python file.


回答 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]。然后,它检查该文件是否确实存在,然后使用上下文管理器将其打开,这样您就不必记住要在文件句柄上调用close了。恕我直言,从长远来看,这样做会为您省去很多痛苦。

I’d do it this way:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

The above code builds an absolute path to the file using abspath and is equivalent to using normpath(join(os.getcwd(), path)) [that’s from the pydocs]. It then checks if that file actually exists and then uses a context manager to open it so you don’t have to remember to call close on the file handle. IMHO, doing it this way will save you a lot of pain in the long run.