如何可靠地在当前运行脚本的目录中打开文件
如何可靠地在当前运行脚本的目录中打开文件
在以前,我通常通过以下命令打开与当前运行的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__)
返回用于调用脚本的路径,这可能是相对路径甚至是空白(如果脚本在当前工作目录中)。此外,当在IDLE或PythonWin中运行脚本时,__file__
不存在。sys.path[0]
和os.path.abspath(os.path.dirname(sys.argv[0]))
似乎返回脚本目录。我不确定这两者之间是否有任何区别。
编辑:
我刚刚意识到,更准确地说,我想做的是"在包含模块的同一目录中打开文件"。换句话说,如果我导入了一个位于另一个目录中的模块,并且该模块打开了一个文件,我希望它在模块的目录中查找该文件。我不认为我找到的任何方法能够实现这一点...
如何可靠地在当前运行脚本的目录中打开文件
问题的出现原因:
根据Python文档的描述,当程序启动时,sys.path[0]
是包含调用Python解释器的脚本的目录。但是,如果脚本目录不可用(例如,如果通过交互方式调用解释器或者从标准输入读取脚本),sys.path[0]
将为空字符串,这会导致Python首先在当前目录中搜索模块。这就是问题的出现原因。
解决方法:
- 如果你是在终端上运行脚本,sys.path[0]
就是你要找的路径。
- 要获取文件的完整路径,可以使用os.path.join(sys.path[0], 'some file.txt')
。这样可以在所有系统上正确处理空格和斜杠。
- 不要依赖sys.argv
来告诉你脚本的名称!在确定脚本目录时,使用__file__
。
要可靠地在当前运行脚本的目录中打开文件,可以使用sys.path[0]
或__file__
来获取脚本所在的目录,并使用os.path.join()
来拼接文件的完整路径。这样可以确保在不同的环境中都能正确地打开文件。
Python 3.4版本引入了pathlib模块,并且下面的代码可以可靠地在当前脚本的同一目录中打开一个文件:
from pathlib import Path p = Path(__file__).with_name('file.txt') with p.open('r') as f: print(f.read())
如果你需要将文件路径作为字符串传递给某些类似于open的API,可以使用absolute()方法来获取它:
p = Path(__file__).with_name('file.txt') filename = p.absolute()
需要注意的是,Python的REPL(交互式解释器)如使用python命令或ipython运行时不会暴露__file__属性。
如何可靠地在当前运行脚本的目录中打开文件
在编写Python脚本时,经常需要在与当前脚本相同的目录中打开文件。然而,处理文件路径时可能会遇到一些问题。本文将介绍出现这些问题的原因以及解决方法。
在处理文件路径时,我们通常使用以下代码来获取当前脚本所在的目录:
__location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__)))
其中,`__file__`代表当前脚本的文件路径。此代码使用了`os.path.join()`函数将当前工作目录与`os.path.dirname(__file__)`拼接在一起,然后使用`os.path.realpath()`函数获取其绝对路径。
然而,需要注意的是,如果路径是绝对路径,那么所有在该路径左侧的路径都会被忽略。因此,当`dirname(__file__)`返回的是绝对路径时,`getcwd()`函数返回的当前工作目录会被忽略。
另外,`realpath()`函数还可以解析符号链接。这在在Linux系统上使用setuptools部署时非常有用(脚本被符号链接到`/usr/bin/`目录下)。
要在当前目录中打开文件,可以使用以下代码:
f = open(os.path.join(__location__, 'bundled-resource.jpg')) # ...
以上代码将文件路径拼接到`__location__`变量中,然后使用`open()`函数打开文件。
此方法在在Windows和Linux上使用Django应用程序时非常有效。
如果不能使用`__file__`,则可以使用`sys.argv[0]`来代替`dirname(__file__)`。其他部分的代码应该如预期地工作。我喜欢使用`__file__`,因为在库代码中,`sys.argv[0]`可能根本不指向你的代码,特别是当通过某个第三方脚本导入时。
然而,使用`__file__`存在一个问题,那就是当脚本直接从解释器运行时和当脚本被导入时,结果会有所不同。要了解`file`和`sys.argv[0]`之间的区别,请参考我的答案。
因此,使用`realpath( join( getcwd(), dirname(__file__) ))`可以解决Zimm3r答案中描述的这种变化的问题。
不需要使用`getcwd()`,因为`os.path.abspath()`函数已经为我们做了这个工作。而`os.path.realpath()`函数会调用`os.path.abspath()`函数。
更现代的做法是使用`pathlib.Path(__file__).parent / 'Some file.txt'`来打开文件。
如果要打开位于当前目录的子文件夹中的文件(例如"Images/bundled-resource.jpg"),可以将文件路径适配为:
f = open(os.path.join(__location__, 'Images', 'bundled-resource.jpg')) # ...
以上就是解决在当前运行脚本的目录中可靠地打开文件的方法。