我想知道在Python中确定当前脚本目录的最佳方法是什么。
我发现,由于调用Python代码的方法很多,很难找到一个好的解决方案。
以下是一些问题:
如果脚本使用exec, execfile执行,则__file__未定义
__module__只在模块中定义
用例:
。/ myfile.py
python myfile.py
/ somedir / myfile.py
python somedir / myfile.py
Execfile ('myfile.py')(来自另一个脚本,可以位于另一个目录,并且可以有另一个当前目录。
我知道没有完美的解决方案,但我正在寻找解决大多数情况的最佳方法。
最常用的方法是os.path.dirname(os.path.abspath(__file__)),但如果你用exec()从另一个脚本执行脚本,这就行不通了。
警告
任何使用当前目录的解决方案都会失败,这可以根据脚本调用的方式有所不同,也可以在运行的脚本中更改。
第一. .如果我们在讨论注入匿名代码的方法,这里会遗漏一些用例。
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 '\t >> level 2'
exec("import sys; sys.path.append('/tmp'); import deepest")
print '\t << level 2'
/ tmp / deepest.py
print '\t\t >> level 3'
print '\t\t\t I can see the earths core.'
print '\t\t << 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'。
对于更高级别的“模块”加载挂钩,你可能会使用use
sys。path_hooks(由PyMOTW提供的文章)。
这就是我所能想到的。
这是一个部分的解决方案,仍然比迄今为止发表的所有解决方案都要好。
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]不能工作,如果你使用python -c "execfile('path-tester.py')"执行脚本,将返回-c "
我在https://gist.github.com/1385555上发布了完整的测试,欢迎您对其进行改进。
#!/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())
It works on CPython, Jython, Pypy. It works if the script is executed using execfile() (sys.argv[0] and __file__ -based solutions would fail here). It works if the script is inside an executable zip file (/an egg). It works if the script is "imported" (PYTHONPATH=/path/to/library.zip python -mscript_to_run) from a zip file; it returns the archive path in this case. It works if the script is compiled into a standalone executable (sys.frozen). It works for symlinks (realpath eliminates symbolic links). It works in an interactive interpreter; it returns the current working directory in this case.
在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
注意:这个答案现在是一个包(也具有安全的相对导入功能)
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__", "./_")如果globals()中存在键"__file__",则返回键"__file__"的值,否则返回提供的默认值"./_"。
其余的代码只是将__file__(或"./_")展开为一个绝对文件路径,然后返回该文件路径的基目录。
选择:
如果你确定__file__对你的周边代码可用,你可以简化为:
>= Python 3.4: this_dir = Path(__file__).absolute().parent
>= Python 2: this_dir = os.path.dirname(os.path.abspath(__file__))
如果__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
with
# -- main.py --
import script1.py
import script2.py
输出结果为:
~/test_dir/test_subdir
~/test_dir
要获得包含当前脚本的目录的绝对路径,您可以使用:
from pathlib import Path
absDir = Path(__file__).parent.resolve()
请注意,.resolve()调用是必需的,因为它使路径成为绝对路径。如果没有resolve(),您将获得类似于'.'的东西。
这个解决方案使用pathlib,它自v3.4(2014)以来一直是Python的stdlib的一部分。与使用os的其他解决方案相比,这是更可取的。
官方的pathlib文档有一个有用的表,将旧的操作系统函数映射到新函数:https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module