ChatGPT解决这个技术问题 Extra ChatGPT

如何可靠地打开与当前正在运行的脚本位于同一目录中的文件

我曾经通过简单地使用以下命令打开与当前运行的 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 中)。此外,当脚本在 IDLE 或 PythonWin 中运行时,__file__ 不存在。

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

编辑:

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

“错误的目录”是不正确的分析。双击或否,脚本在您当前的工作目录中运行,而不是您保存脚本的目录。
如果您假设“当前工作目录”是指您的脚本的存储位置,那就错了;这是两件不同的事情。或许另见What exactly is current working directory?

H
Hubert Grzeskowiak

我总是使用:

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

join() 调用将当前工作目录添加到前面,但文档说如果某个路径是绝对路径,则它剩下的所有其他路径都将被删除。因此,当 dirname(__file__) 返回绝对路径时,会删除 getcwd()

此外,realpath 调用会解析符号链接(如果找到)。这避免了在 Linux 系统上使用 setuptools 部署时的麻烦(脚本符号链接到 /usr/bin/ —— 至少在 Debian 上)。

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

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

我用它来将资源与 Windows 和 Linux 上的几个 Django 应用程序捆绑在一起,它就像一个魅力!


如果无法使用 __file__,则使用 sys.argv[0] 而不是 dirname(__file__)。其余的应该按预期工作。我喜欢使用 __file__,因为在库代码中,sys.argv[0] 可能根本不指向您的代码,尤其是通过某些 3rd 方脚本导入时。
问题在于,如果您正在运行的文件是直接来自中断器还是导入的,它会有所不同。有关文件和 sys.argv[0] 之间的差异,请参阅我的答案
那么说 Zimm3r 的答案中描述的变化是通过使用这里描述的 realpath( join( getcwd(), dirname(__file__) )) 来解决的,这是否正确?
无需使用 getcwd(),因为 os.path.abspath() 函数为您完成此操作os.path.realpath() 调用 os.path.abspath()
更现代的方法是通过 open(pathlib.Path(__file__).parent / 'Some file.txt')
P
P i

引用 Python 文档:

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

如果您从终端运行脚本,sys.path[0] 就是您要查找的内容。

但是,如果您有:

barpath/bar.py
    import foopath.foo

foopath/foo.py
    print sys.path[0]  # you get barpath

所以小心!


对于文件的完整路径:os.path.join(sys.path[0], 'some file.txt')。这应该在所有系统上正确处理空格和斜杠。
这是第一个问题的答案,而不是编辑后的答案。
sys.argv[0] 设置为父进程告诉操作系统将其设置为的任何值。使用 #!/usr/env python 作为名为 test.py 的脚本的第一行,使文件可执行,然后使用 alias foo test.py。或创建文件的符号链接。不管怎样,现在 sys.argv[0] 将是错误的。或者使用其中一个 os.exec*() functions 运行脚本并为第一个参数选择您自己的值。不要依赖 sys.argv 告诉你脚本的名称!确定脚本的目录时,使用 __file__
J
João Haas

在 Python 3.4 上,添加了 pathlib module,以下代码将可靠地打开与当前脚本位于同一目录中的文件:

from pathlib import Path

p = Path(__file__).with_name('file.txt')
with p.open('r') as f:
    print(f.read())

如果您只需要在类似 open 的 API 中使用目录路径,则可以使用 parent.absolute() 获取它:

p = Path(__file__)
dir_abs = p.parent.absolute()  # Will return the executable's directory

Z
Zimm3r

好的,这就是我所做的

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


这是实现反映文档的详细证明。 __file__应该“总是给你当前文件的路径”,而sys.argv[0]应该“总是给启动脚本的路径”过程”。在任何情况下,在被调用的脚本中使用 __file__ 总能为您提供精确的结果。
如果您在脚本的顶层引用了 __file__,它将按预期工作。
sys.argv[0] 设置为父进程告诉操作系统将其设置为的任何值。使用 #!/usr/env python 作为 test.py 的第一行,使文件可执行,然后使用 alias foo test.py。或创建文件的符号链接。不管怎样,现在 sys.argv[0] 将是错误的。或者使用其中一个 os.exec*() functions 运行脚本并为第一个参数选择您自己的值。不要依赖 sys.argv 告诉你脚本的名称!确定脚本的目录时,使用 __file__
M
Martin

我通常使用以下内容。它也适用于测试以及可能的其他用例。

with open(os.path.join(os.path.dirname(__file__), 'some_file.txt'), 'r') as f:

https://stackoverflow.com/questions/10174211/how-to-make-an-always-relative-to-current-module-file-path
推荐这个答案


F
Fernando Nogueira

你能像这样尝试这种简单的方法吗:

    import os

    my_local_file = os.path.join(os.path.dirname(__file__), 'some_file.txt')

    f = open(my_local_file,  "r")
    my_local_data = f.read()

R
Raoul HATTERER

因为我在尝试使用 emacs 中的 __file__sys.argv[0] 时遇到错误,所以我这样做:

from inspect import getfile
from pathlib import Path


script_path = getfile(lambda: None)
print(script_path)
parent_path = Path(script_path).parent
print(parent_path)

with open(parent_path/'Some file.txt', 'r') as obFile:
    print(obFile.read())

T
Tomas Cufaro

在尝试了所有这些解决方案之后,我仍然遇到了不同的问题。所以我发现最简单的方法是创建一个python文件:config.py,其中包含一个包含文件绝对路径的字典并将其导入到脚本中。就像是

import config as cfg 
import pandas as pd 
pd.read_csv(cfg.paths['myfilepath'])

config.py 里面有:

paths = {'myfilepath': 'home/docs/...'}

它不是自动的,但是当您必须在不同的目录或不同的机器上工作时,它是一个很好的解决方案。


问题是明确要求 Python 脚本的目录,而不是硬编码值,无论您如何“导入”它
d
dcolish

我会这样做:

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,然后使用上下文管理器打开它,这样您就不必记住在文件句柄上调用 close。恕我直言,从长远来看,这样做会为您节省很多痛苦。


这并不能回答发帖人的问题。 dln385 明确表示如果脚本不在当前目录中,os.path.abspath 不会解析与脚本位于同一文件夹中的文件的路径。
啊!我假设用户正在与他们想要读取的文件相同的目录中运行此脚本,而不是在他们的 PYTHONPATH 中某些东西的模块目录中。那会教我做出假设...
abspath 将不起作用,因为 python 运行时不可能使用这样的函数在 OS 文件系统上搜索。