如何正确确定当前脚本目录?

bog*_*dan 229 python pythonpath dirname

我想看看在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().

警告

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

bob*_*nce 205

os.path.dirname(os.path.abspath(__file__))
Run Code Online (Sandbox Code Playgroud)

确实是你最好的.

使用exec/ 执行脚本是不常见的execfile; 通常您应该使用模块基础结构来加载脚本.如果您必须使用这些方法,我建议您在传递给脚本时进行设置__file__,globals以便它可以读取该文件名.

没有其他方法可以在执行代码中获取文件名:正如您所注意到的,CWD可能处于完全不同的位置.

  • 使用pathlib更新:pathlib.Path(__file__).resolve()/'..' (5认同)
  • `os.path.dirname(os.path.abspath(__file__))` 绝对不是你能得到的最好的。真的,那根本就不是尝试。正如@JeffEllen建议的那样,请参阅[this](/sf/answers/1294240321/)和[this](/sf/answers/434692611/)以获得更安全、更强大的信息,以及更便携的替代品。 (5认同)
  • 永远不要把话说绝了?根据这个:/sf/answers/1294240321/ 回答一个跨平台的解决方案是 abspath(getsourcefile(lambda:0))?或者还有什么我想念的吗? (2认同)

Sve*_*ach 124

如果您真的想要涵盖通过调用脚本的情况execfile(...),您可以使用该inspect模块来推断文件名(包括路径).据我所知,这适用于您列出的所有情况:

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
Run Code Online (Sandbox Code Playgroud)

  • @Ryan LOL,如果你能够定义一个多平台的"已知位置"并且该模块附带的话,那将会很棒.我准备打赌,唯一安全的位置是脚本位置.请注意,这不是脚本应该写入此位置的内容,但是对于读取数据,它是安全的. (13认同)
  • @sorin:我在运行脚本之前尝试过`chdir()`; 它产生了正确的结果.我试过从另一个目录调用脚本,它也有效.结果与[`inspect.getabsfile()`'解决方案](http://stackoverflow.com/a/22881871/4279)相同. (6认同)
  • 我认为这确实是最强大的方法,但我质疑OP对此的需求.我经常看到开发人员在相对于执行模块的位置使用数据文件时执行此操作,但IMO数据文件应放在已知位置. (4认同)
  • 不过,解决方案并不好,只要尝试在函数之前调用 chdir() ,就会改变结果。从另一个目录调用 python 脚本也会改变结果,所以这不是一个好的解决方案。 (2认同)
  • `os.path.expanduser("〜")`是获取用户目录的跨平台方式.不幸的是,这不是Windows在哪里粘贴应用程序数据的最佳做法. (2认同)

jfs*_*jfs 40

#!/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())
Run Code Online (Sandbox Code Playgroud)

它适用于CPython,Jython,Pypy.如果使用execfile()(sys.argv[0]并且__file__基于此的解决方案将在此处失败)执行脚本,它可以工作.如果脚本在可执行的zip文件(/ egg)中,它就可以工作.如果脚本是PYTHONPATH=/path/to/library.zip python -mscript_to_run从zip文件"导入"(),它可以工作; 在这种情况下,它返回归档路径.如果脚本编译为独立的可执行文件(sys.frozen),它就可以工作.它适用于符号链接(realpath消除符号链接).它适用于交互式口译员; 在这种情况下,它返回当前的工作目录.

  • [`inspect` 的文档](https://docs.python.org/2/library/inspect.html) 中没有提到 `getabsfile(..)` 是否有任何原因?它出现在该页面链接的源中。 (2认同)

Eug*_*ash 22

在Python 3.4+中,您可以使用更简单的pathlib模块:

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
Run Code Online (Sandbox Code Playgroud)

  • 哦,我误解了,你的意思是避免检查模块,只是使用类似`parent = Path(__ file __).rescolution().parent`这样的东西更好. (6认同)
  • 优秀的简约! (2认同)
  • 你可能可以使用`Path(__ file __)`(不需要`inspect`模块). (2认同)
  • @Dut A.您应该使用[`.joinpath()`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.joinpath)(或`/`运算符) ,而不是`+`. (2认同)
  • 注意:如果您想要没有符号链接的绝对路径,则必须使用resolve()。文档[此处](https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve) (2认同)

Sim*_*her 9

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

https://github.com/heetbeet/locate

$ pip install locate

$ python
>>> from locate import this_dir
>>> print(this_dir())
C:/Users/simon
Run Code Online (Sandbox Code Playgroud)

对于.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
Run Code Online (Sandbox Code Playgroud)

Python 2(及更高版本):

import os
this_dir = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))
Run Code Online (Sandbox Code Playgroud)

解释:

  • globals()以字典形式返回所有全局变量。
  • .get("__file__", "./_")"__file__"如果键存在于 中,则返回该键的值globals(),否则返回提供的默认值"./_"
  • 其余代码只是将__file__(或"./_") 扩展为绝对文件路径,然后返回文件路径的基目录。

选择:

如果您确定__file__周围代码可用,则可以简化为:

  • >=Python 3.4this_dir = Path(__file__).absolute().parent
  • >=Python 2this_dir = os.path.dirname(os.path.abspath(__file__))


Wil*_*hen 8

import os
cwd = os.getcwd()
Run Code Online (Sandbox Code Playgroud)

做你想做的事?我不确定“当前脚本目录”到底是什么意思。您给出的用例的预期输出是什么?

  • 这没有帮助。我相信 @bogdan 正在寻找位于调用堆栈顶部的脚本的目录。即在他/她的所有情况下,它应该打印“myfile.py”所在的目录。然而你的方法只会打印调用 `exec('myfile.py')` 的文件的目录,与 `__file__` 和 `sys.argv[0]` 相同。 (3认同)

Ron*_*ian 6

os.path...方法是Python 2中的“完成的事情”。

在Python 3中,您可以找到脚本目录,如下所示:

from pathlib import Path
cwd = Path(__file__).parents[0]
Run Code Online (Sandbox Code Playgroud)

  • 或者只是`Path(__ file __)。parent`。但是`cwd`是用词不当,不是_current工作目录_,而是_file的directory_。它们可能是相同的,但通常并非如此。 (7认同)

wim*_*wim 5

只需使用os.path.dirname(os.path.abspath(__file__))并仔细检查是否真的需要使用的情况exec.如果您无法将脚本用作模块,则可能是设计有问题的迹象.

请记住Python#8的Zen,如果您认为对于必须工作的用例有一个很好的论据exec,请告诉我们有关问题背景的更多细节.

  • 如果不使用exec()运行,则会丢失调试器上下文.exec()也应该比开始一个新进程快得多. (2认同)