我曾经通过简单地使用以下命令打开与当前运行的 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])) 似乎返回脚本目录。我不确定这两者之间是否有任何区别。
编辑:
我刚刚意识到,我想要做的更好地描述为“在与包含模块相同的目录中打开一个文件”。换句话说,如果我导入了我在另一个目录中编写的模块,并且该模块打开了一个文件,我希望它在模块的目录中查找该文件。我认为我发现的任何东西都无法做到这一点......
我总是使用:
__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 应用程序捆绑在一起,它就像一个魅力!
引用 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__
。
在 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
好的,这就是我所做的
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__
。
我通常使用以下内容。它也适用于测试以及可能的其他用例。
with open(os.path.join(os.path.dirname(__file__), 'some_file.txt'), 'r') as f:
你能像这样尝试这种简单的方法吗:
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()
因为我在尝试使用 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())
在尝试了所有这些解决方案之后,我仍然遇到了不同的问题。所以我发现最简单的方法是创建一个python文件:config.py,其中包含一个包含文件绝对路径的字典并将其导入到脚本中。就像是
import config as cfg
import pandas as pd
pd.read_csv(cfg.paths['myfilepath'])
config.py 里面有:
paths = {'myfilepath': 'home/docs/...'}
它不是自动的,但是当您必须在不同的目录或不同的机器上工作时,它是一个很好的解决方案。
我会这样做:
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。恕我直言,从长远来看,这样做会为您节省很多痛苦。
os.path.abspath
不会解析与脚本位于同一文件夹中的文件的路径。
__file__
,则使用sys.argv[0]
而不是dirname(__file__)
。其余的应该按预期工作。我喜欢使用__file__
,因为在库代码中,sys.argv[0]
可能根本不指向您的代码,尤其是通过某些 3rd 方脚本导入时。realpath( join( getcwd(), dirname(__file__) ))
来解决的,这是否正确?getcwd()
,因为os.path.abspath()
函数为您完成此操作。os.path.realpath()
调用os.path.abspath()
。open(pathlib.Path(__file__).parent / 'Some file.txt')