我想知道在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()从另一个脚本执行脚本,这就行不通了。

警告

任何使用当前目录的解决方案都会失败,这可以根据脚本调用的方式有所不同,也可以在运行的脚本中更改。


当前回答

注意:这个答案现在是一个包(也具有安全的相对导入功能)

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__))

其他回答

因为以前的答案需要你导入一些模块,我想我会写一个答案,不。如果您不想导入任何东西,请使用下面的代码。

this_dir = '/'.join(__file__.split('/')[:-1])
print(this_dir)

如果脚本在/path/to/script.py上,那么将打印/path/to。注意,这将在终端上抛出错误,因为没有执行任何文件。这基本上是从__file__解析目录,删除它的最后一部分。在本例中,/script.py被删除以生成输出/路径/到。

这应该在大多数情况下工作:

import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
print(__import__("pathlib").Path(__file__).parent)

第一. .如果我们在讨论注入匿名代码的方法,这里会遗漏一些用例。

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提供的文章)。

这就是我所能想到的。

只需使用os.path.dirname(os.path.abspath(__file__)),并非常仔细地检查是否真的需要使用exec。如果不能将脚本作为模块使用,这可能是设计有问题的标志。

请记住Python #8的Zen,如果您认为用例必须适用于exec,那么请让我们了解有关问题背景的更多细节。