如何正确确定当前脚本目录?[重复]
- 2024-11-29 08:42:00
- admin 原创
- 123
问题描述:
我想看看在 Python 中确定当前脚本目录的最佳方法是什么。
我发现,由于调用 Python 代码的方式很多,很难找到一个好的解决方案。
这里存在一些问题:
__file__
如果使用 执行脚本则未定义exec
,execfile
__module__
仅在模块中定义
使用案例:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(来自另一个脚本,该脚本可以位于另一个目录中,并且可以有另一个当前目录。
我知道没有完美的解决方案,但我正在寻找解决大多数情况的最佳方法。
最常用的方法是,os.path.dirname(os.path.abspath(__file__))
但如果您使用从另一个脚本执行脚本,这实际上不起作用exec()
。
警告
任何使用当前目录的解决方案都将失败,这可能根据脚本的调用方式而有所不同,也可以在正在运行的脚本中进行更改。
解决方案 1:
os.path.dirname(os.path.abspath(__file__))
确实是你能得到的最好的结果。
使用exec
/执行脚本并不常见execfile
;通常您应该使用模块基础结构来加载脚本。如果您必须使用这些方法,我建议您__file__
在globals
传递给脚本的 中进行设置,以便它可以读取该文件名。
没有其他方法可以在执行的代码中获取文件名:正如您注意到的,CWD 可能位于完全不同的地方。
解决方案 2:
如果您确实想涵盖通过 调用脚本的情况execfile(...)
,则可以使用inspect
模块推断文件名(包括路径)。据我所知,这适用于您列出的所有情况:
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
解决方案 3:
在 Python 3.4+ 中,你可以使用更简单的pathlib
模块:
from inspect import currentframe, getframeinfo
from pathlib import Path
filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
您还可以使用__file__
(当可用时)来inspect
完全避免使用该模块:
from pathlib import Path
parent = Path(__file__).resolve().parent
解决方案 4:
#!/usr/bin/env python
import inspect
import os
import sys
def get_script_dir(follow_symlinks=True):
if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
path = os.path.abspath(sys.executable)
else:
path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
path = os.path.realpath(path)
return os.path.dirname(path)
print(get_script_dir())
execfile()
它适用于 CPython、Jython、Pypy。如果使用(sys.argv[0]
和__file__
- 解决方案在此处会失败)执行脚本,则它可以工作。如果脚本位于可执行 zip 文件 (/an egg)中,则它可以工作。如果脚本是PYTHONPATH=/path/to/library.zip python -mscript_to_run
从 zip 文件“导入”( ) 的,则它可以工作;在这种情况下,它会返回存档路径。如果脚本被编译为独立可执行文件 ( sys.frozen
),则它可以工作。它适用于符号链接 (realpath
消除符号链接)。它在交互式解释器中工作;在这种情况下,它会返回当前工作目录。
解决方案 5:
该os.path...
方法是 Python 2 中的“已完成的事情”。
在 Python 3 中,你可以按如下方式找到脚本的目录:
from pathlib import Path
script_path = Path(__file__).parent
解决方案 6:
注意:这个答案现在是一个包(也具有安全的相对导入功能)
https://github.com/heetbeet/locate
$ pip install locate
$ python
>>> from locate import this_dir
>>> print(this_dir())
C:/Users/simon
对于.py
脚本以及交互式使用:
我经常使用脚本的目录(用于访问与脚本一起存储的文件),但我也经常在交互式 shell 中运行这些脚本以进行调试。我将其定义this_dir
为:
运行或导入
.py
文件时,文件的基本目录。这始终是正确的路径。运行笔记本时
.ipyn
,当前工作目录。这始终是正确的路径,因为 Jupyter 将工作目录设置为.ipynb
基目录。在 REPL 中运行时,当前工作目录。嗯,当代码与文件分离时,实际的“正确路径”是什么?相反,在调用 REPL 之前,您有责任更改为“正确路径”。
Python 3.4 (及以上版本):
from pathlib import Path
this_dir = Path(globals().get("__file__", "./_")).absolute().parent
Python 2 (及以上版本):
import os
this_dir = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))
解释:
globals()
将所有全局变量作为字典返回。.get("__file__", "./_")
`"__file__"如果 中存在 则返回键的值
globals(),否则返回提供的默认值
"./_"`。代码的其余部分只是扩展
__file__
(或"./_"
)为绝对文件路径,然后返回文件路径的基目录。
选择:
如果你确定__file__
周围的代码可以使用,那么你可以简化如下:
>=Python 3.4:
this_dir = Path(__file__).absolute().parent
>=Python 2:
this_dir = os.path.dirname(os.path.abspath(__file__))
解决方案 7:
会
import os
cwd = os.getcwd()
做你想做的事?我不确定你所说的“当前脚本目录”到底是什么意思。对于你给出的用例,预期的输出是什么?
解决方案 8:
只需使用os.path.dirname(os.path.abspath(__file__))
并仔细检查是否真的需要使用这种情况exec
。如果您无法将脚本用作模块,则可能是设计存在问题的迹象。
请记住Python 之禅 #8,如果您认为对于某个用例来说,有一个很好的论据证明它必须适用于exec
,那么请让我们知道有关问题背景的更多细节。
解决方案 9:
要获取包含当前脚本的目录的绝对路径,您可以使用:
from pathlib import Path
absDir = Path(__file__).parent.resolve()
请注意.resolve()
调用是必需的,因为这是使路径绝对化的调用。如果没有resolve()
,您将获得类似 的内容'.'
。
此解决方案使用pathlib
,它是自 v3.4 (2014) 以来 Python stdlib 的一部分。与使用 的其他解决方案相比,这是更可取的os
。
官方pathlib
文档有一个有用的表格,将旧os
函数映射到新函数:https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module
解决方案 10:
首先,如果我们讨论注入匿名代码的方法,这里缺少几个用例。
code.compile_command()
code.interact()
imp.load_compiled()
imp.load_dynamic()
imp.load_module()
__builtin__.compile()
loading C compiled shared objects? example: _socket?)
但真正的问题是,你的目标是什么 - 你是想强制执行某种安全措施吗?还是你只是对正在加载的内容感兴趣。
如果您对安全性感兴趣,通过 exec/execfile 导入的文件名无关紧要 - 您应该使用rexec,它提供以下内容:
此模块包含 RExec 类,支持 r_eval()、r_execfile()、r_exec() 和 r_import() 方法,这些方法是标准 Python 函数 eval()、execfile() 以及 exec 和 import 语句的受限版本。在此受限环境中执行的代码只能访问被视为安全的模块和函数;您可以根据需要将 RExec 子类化以添加或删除功能。
然而,如果这更像是一种学术追求......这里有几个愚蠢的方法,你也许可以深入研究一下......
示例脚本:
./deep.py
print ' >> level 1'
execfile('deeper.py')
print ' << level 1'
./deeper.py
print ' >> level 2'
exec("import sys; sys.path.append('/tmp'); import deepest")
print ' << level 2'
/tmp/deepest.py
print ' >> level 3'
print ' I can see the earths core.'
print ' << level 3'
./codespy.py
import sys, os
def overseer(frame, event, arg):
print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename)
sys.settrace(overseer)
execfile("deep.py")
sys.exit(0)
输出
loaded(/Users/synthesizerpatel/deep.py)
>> level 1
loaded(/Users/synthesizerpatel/deeper.py)
>> level 2
loaded(/Users/synthesizerpatel/<string>)
loaded(/tmp/deepest.py)
>> level 3
I can see the earths core.
<< level 3
<< level 2
<< level 1
当然,这是一种资源密集型的方法,你需要跟踪所有代码。效率不高。但我认为这是一种新颖的方法,因为即使你深入嵌套,它仍会继续工作。你不能覆盖“eval”。虽然你可以覆盖 execfile()。
请注意,此方法仅涵盖 exec/execfile,而不涵盖“import”。对于更高级别的“模块”加载挂钩,您可能能够使用
sys.path_hooks(由 PyMOTW 提供撰写)。
这就是我所知道的全部内容。
解决方案 11:
这是一个部分解决方案,但仍然比迄今为止发布的所有解决方案都要好。
import sys, os, os.path, inspect
#os.chdir("..")
if '__file__' not in locals():
__file__ = inspect.getframeinfo(inspect.currentframe())[0]
print os.path.dirname(os.path.abspath(__file__))
现在,所有调用都可以正常工作,但如果有人使用它chdir()
来更改当前目录,这也会失败。
笔记:
sys.argv[0]
不会起作用,-c
如果你使用以下命令执行脚本,将会返回python -c "execfile('path-tester.py')"
我在https://gist.github.com/1385555发布了完整的测试,欢迎大家对其进行改进。
解决方案 12:
在大多数情况下这应该有效:
import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
解决方案 13:
希望这会有所帮助: - 如果您从任何地方运行脚本/模块,您将能够访问__file__
变量,该变量是代表脚本位置的模块变量。
另一方面,如果您正在使用解释器,则您无权访问该变量,您将从中获得一个名称NameError
,并且os.getcwd()
如果您从其他地方运行该文件,它将为您提供错误的目录。
这个解决方案应该能在所有情况下满足您的需求:
from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))
我还没有彻底测试过它,但它解决了我的问题。
解决方案 14:
如果__file__
可用:
# -- script1.py --
import os
file_path = os.path.abspath(__file__)
print(os.path.dirname(file_path))
对于那些我们希望能够从解释器运行命令或获取运行脚本的位置的路径:
# -- script2.py --
import os
print(os.path.abspath(''))
这在解释器中是有效的。但是在脚本中运行(或导入)时,它会给出运行脚本的位置的路径,而不是包含打印脚本的目录的路径。
例子:
如果你的目录结构是
test_dir (in the home dir)
├── main.py
└── test_subdir
├── script1.py
└── script2.py
和
# -- main.py --
import script1.py
import script2.py
输出为:
~/test_dir/test_subdir
~/test_dir
解决方案 15:
由于之前的答案要求您导入一些模块,因此我想我会写一个不需要的答案。如果您不想导入任何内容,请使用下面的代码。
this_dir = '/'.join(__file__.split('/')[:-1])
print(this_dir)
如果脚本已打开/path/to/script.py
,则将打印/path/to
。请注意,这将在终端上抛出错误,因为没有执行任何文件。这基本上会解析目录,从而__file__
删除其最后一部分。在本例中,/script.py
删除 以产生输出/path/to
。
解决方案 16:
print(__import__("pathlib").Path(__file__).parent)
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)