如何获取当前正在执行的Python文件的路径和名称?

560
我有一些脚本调用其他脚本文件,但我需要获取当前正在运行的文件的文件路径。
例如,假设我有三个文件。使用execfile
- `script_1.py` 调用 `script_2.py`。 - `script_2.py` 又调用 `script_3.py`。
在不必从 `script_2.py` 传递该信息作为参数的情况下,我如何从 `script_3.py` 的代码中获取 script_3.py 的文件名和路径?
(执行 `os.getcwd()` 返回的是原始启动脚本的文件路径,而不是当前文件的路径。)

2
os.path.realpath(file) - Girish Gupta
如何正确确定当前脚本目录? - jfs
这个回答是否回答了你的问题?如何正确确定当前脚本目录? - outis
请参阅什么是当前工作目录? - tripleee
26个回答

622
__file__

像其他人说的那样,您可能还想使用os.path.realpath来消除符号链接:

import os

os.path.realpath(__file__)

16
这种方法需要小心,因为有时候__file__会返回'script_name.py',而其他时候会返回'script_name.pyc',所以输出不稳定。 - mechatroner
27
但由于我们只使用了这个文件的路径,因此这并不重要。 - Uwe Koloska
5
这很奇怪:在命令行中以字符串形式运行"__file__"时,它会给出cmd所在的目录,但是不加引号的__file__会给出源文件的路径……为什么会这样? - muon
12
@muon:这里没有对传入的文件名字符串是否存在进行检查,由于文件路径是相对于当前工作目录的,因此os.path.realpath函数假设路径中的“dir”部分是当前工作目录。 os.path.dirname(os.path.realpath(__file__)) 返回包含该文件的目录。 os.path.dirname(os.path.realpath("__file__")) 返回当前工作目录。 os.path.dirname(os.path.realpath("here_be_dragons")) 同样返回当前工作目录。 - Jamie Bull
名称'file'未定义 - undefined
我刚刚在这个答案中写了一个大的扩展,点击这里,以获取FULL_PATH_TO_SCRIPTSCRIPT_DIRECTORYFILENAMEFILENAME_NO_EXTENSIONFILENAME_EXTENSIONHOME_DIR_USERHOME_DIR_SCRIPT - undefined

286

p1.py:

execfile("p2.py")

p2.py:

import inspect, os
print (inspect.getfile(inspect.currentframe())) # script filename (usually with path)
print (os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) # script directory

14
注意:该调用在不同的环境下可能会产生不同的结果。请考虑接受下面Usagi的回答:https://dev59.com/T3VD5IYBdhLWcg3wNY1Z#6628348 - faraday
3
@faraday: 你能提供一个例子吗?我已经使用inspect.getabsfile()回答了类似的问题,并且对我尝试过的所有情况都起作用。 - jfs
4
用户13993对此表述得更好。 - osirisgothra
6
user13993确实说对了。应该是os.path.realpath(__file__) - uchuugaka
3
如果传入一个模块对象,inspect.getfile() 函数会返回 __file__ 属性。而 inspect.currentframe() 函数则返回该模块。因此,这种方式是昂贵的,相当于说出了 __file__ - Martijn Pieters
显示剩余6条评论

104

更新 2018-11-28:

以下是使用 Python 2 和 3 进行实验的摘要:

main.py - 运行 foo.py
foo.py - 运行 lib/bar.py
lib/bar.py - 打印文件路径表达式。

| Python | Run statement       | Filepath expression                    |
|--------+---------------------+----------------------------------------|
|      2 | execfile            | os.path.abspath(inspect.stack()[0][1]) |
|      2 | from lib import bar | __file__                               |
|      3 | exec                | (wasn't able to obtain it)             |
|      3 | import lib.bar      | __file__                               |

对于Python 2,建议使用包的方式来导入,可以使用 from lib import bar - 只需在两个文件夹中添加空的 __init__.py 文件即可。

对于Python 3,execfile 已经不存在了 - 最接近的替代方法是 exec(open(<filename>).read()),但会影响堆栈帧。最简单的方法是直接使用 import fooimport lib.bar - 不需要 __init__.py 文件。

另请参阅 Difference between import and execfile


原始答案:

这里有一个基于本主题回答的实验 - 在Windows上使用Python 2.7.10。

基于堆栈的方法似乎是唯一能够给出可靠结果的方法。后面两种方法具有最短的语法,即 -

print os.path.abspath(inspect.stack()[0][1])                   # C:\filepaths\lib\bar.py
print os.path.dirname(os.path.abspath(inspect.stack()[0][1]))  # C:\filepaths\lib

这里希望将以下内容作为函数添加到 sys 中!感谢 @Usagi 和 @pablog。
基于以下三个文件,并从其文件夹中使用 python main.py 运行 main.py(也尝试了使用绝对路径执行文件和从其他文件夹调用)。
C:\filepaths\main.py: execfile('foo.py')
C:\filepaths\foo.py: execfile('lib/bar.py')
C:\filepaths\lib\bar.py:
import sys
import os
import inspect

print "Python " + sys.version
print

print __file__                                        # main.py
print sys.argv[0]                                     # main.py
print inspect.stack()[0][1]                           # lib/bar.py
print sys.path[0]                                     # C:\filepaths
print

print os.path.realpath(__file__)                      # C:\filepaths\main.py
print os.path.abspath(__file__)                       # C:\filepaths\main.py
print os.path.basename(__file__)                      # main.py
print os.path.basename(os.path.realpath(sys.argv[0])) # main.py
print

print sys.path[0]                                     # C:\filepaths
print os.path.abspath(os.path.split(sys.argv[0])[0])  # C:\filepaths
print os.path.dirname(os.path.abspath(__file__))      # C:\filepaths
print os.path.dirname(os.path.realpath(sys.argv[0]))  # C:\filepaths
print os.path.dirname(__file__)                       # (empty string)
print

print inspect.getfile(inspect.currentframe())         # lib/bar.py

print os.path.abspath(inspect.getfile(inspect.currentframe())) # C:\filepaths\lib\bar.py
print os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # C:\filepaths\lib
print

print os.path.abspath(inspect.stack()[0][1])          # C:\filepaths\lib\bar.py
print os.path.dirname(os.path.abspath(inspect.stack()[0][1]))  # C:\filepaths\lib
print

78

我认为这更简洁:

import inspect
print inspect.stack()[0][1]

并且获得与以下信息相同的内容:

print inspect.getfile(inspect.currentframe())

[0]是当前堆栈帧(顶部),[1]是文件名,增加可向后跳转堆栈。

print inspect.stack()[1][1]

使用__file__来获取调用当前帧的脚本文件名。同时,使用[-1]可以让你到达堆栈底部,即原始调用脚本。


5
不建议依赖元组中的位置。当阅读代码时,很难清楚地知道您正在尝试获取什么数据。 - jpmc26
9
如果传入一个模块对象,inspect.getfile()会返回__file__属性。而inspect.currentframe()则会返回该模块。因此,这种方式实际上是一个昂贵的方法来获取__file__ - Martijn Pieters
inspect.stack() 是一个相当昂贵的函数,比仅使用 inspect.currentframe() 更昂贵,并且它也会在模块对象上调用 inspect.getfile() - Martijn Pieters

50
import os
os.path.dirname(__file__) # relative directory path
os.path.abspath(__file__) # absolute file path
os.path.basename(__file__) # the file name only

1
我刚刚尝试了@Pat Notz的评论。我认为你可以通过__file__获取文件名。 - hlin117
在Python3中,os.path.dirname会给你相对路径,从你运行脚本的位置开始计算。例如,在python ../../test.py中,os.path.dirname将是../../而不是脚本的绝对路径。我一直在寻找abspath但是需要去掉脚本的名称。显然,sys.path[0]的第一个元素就是答案。 - aerijman

37

如果您的脚本只包含一个文件,则标记为最佳的建议都是正确的。

如果您想从可能作为模块导入的文件中找出可执行文件的名称(即传递给当前程序的Python解释器的根文件),则需要执行以下操作(假设这在名为foo.py的文件中):

import inspect

print inspect.stack()[-1][1]

因为堆栈上的最后一件事情([-1])是放入其中的第一件事情(堆栈是LIFO / FILO数据结构)。

然后在文件bar.py中,如果您import foo,它将打印bar.py,而不是foo.py,这将是所有这些值的价值:

  • __file__
  • inspect.getfile(inspect.currentframe())
  • inspect.stack()[0][1]

它运行良好,但在Eclipse上的单元测试中不起作用,我得到了/home/user/Softwares/eclipse/plugins/org.python.pydev_4.5.5.201603221110/pysrc。 - hayj
4
这种写法很费钱,其实就是想表达 sys.modules['__main__'].__file__ 的意思。 - Martijn Pieters

28

由于Python 3已经非常流行,我想包括一个pathlib的答案,因为我认为它现在可能是访问文件和路径信息的更好工具。

from pathlib import Path

current_file: Path = Path(__file__).resolve()
如果您正在寻找当前文件的目录,只需在 Path() 语句中添加 .parent 即可:
current_path: Path = Path(__file__).parent.resolve()

15

“the filepath of the file that is currently running within the process”这句话的意思不是很清楚。sys.argv[0]通常包含由Python解释器调用的脚本的位置。 请查看sys文档以获取更多详细信息。

正如@Tim和@Pat Notz所指出的,__file__属性提供了访问以下内容的途径:

如果模块是从文件加载的,则为该模块所加载的文件


1
"print os.path.abspath( file )" 和 "print inspect.getfile( inspect.currentframe() )" 在将 Python 程序传递给 Win32 可执行文件时无法工作。只有 sys.argv[0] 起作用! :) 但是你只能得到名称! - aF.

14
import os
print os.path.basename(__file__)

这将只会给我们文件名。例如,如果文件的绝对路径是c:\abcd\abc.py,则第二行将打印出abc.py


12

我有一个必须在Windows环境下工作的脚本。 以下是我已经完成的代码片段:

import os,sys
PROJECT_PATH = os.path.abspath(os.path.split(sys.argv[0])[0])

这是相当"hacky"的决定。但它不需要外部库,而且在我的情况下是最重要的事情。


1
我必须导入“os,sys”才能实现这一点,但到目前为止,这是最好的答案,它实际上只返回字符串末尾没有文件名的路径。 - emmagras

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接