获取Python中当前脚本的名称

649

我正在尝试获取当前正在运行的Python脚本的名称。

我有一个名为foo.py的脚本,我想这样做以获取脚本名称:

print(Scriptname)

2
Python 2已经过时,请使用print(var)。 - Megan Caithlyn
9
Elia Iliashenko在2010年11月11日9:32提出了问题。 - Tigran
9
Python3.4+中Path(__file__).name表示获取当前文件的文件名(不包含路径)。 - aderchox
14个回答

937
您可以使用__file__来获取当前文件的名称。当在主模块中使用时,这是最初调用的脚本的名称。
如果您想省略目录部分(可能存在),您可以使用os.path.basename(__file__)

26
Python 3.2: "异常 NameError: NameError("全局名称 '__file__' 未定义",)" - sdaau
35
@sdaau:在交互式解释器中,__file__未定义,因为它在那里没有意义。它是由导入实现设置的,因此,如果您使用非标准的导入机制,则可能也会取消设置。 - Sven Marnach
9
至少对于 Python 2.7 版本,我认为需要使用 import os 才能使其正常运行。我会将此添加到答案中。 - Nick Chammas
15
"import os"和 "import os.path" 是完全等价的。 - Sven Marnach
3
@sven-marnach:哦,你是对的。我这些年一直用错了! - cdunn2001
显示剩余11条评论

233
import sys
print(sys.argv[0])

这将会打印出python foo.pyfoo.pypython dir/foo.pydir/foo.py等。它是传递给python的第一个参数。(请注意,在py2exe之后,它将变成foo.exe。)


39
如果你调用python linkfile.py,其中linkfile.py是指向realfile.py的符号链接,sys.argv[0]将会是'linkfile.py',这也许不是你想要的,但却是我预期的。__file__同理:它将是linkfile.py。如果你想从'linkfile.py'中找到'realfile.py',可以尝试使用os.path.realpath('linkfile.py') - Chris Morgan
9
+1 是因为它(a)更整洁,(b)仍然可以在模块中运行(此时 file 变量将是模块文件,而不是执行的文件)。 - robert
1
这个答案很好,因为它也适用于IDLE。需要注意的是,要仅获取文件名,可以编写os.path.basename(sys.argv[0])。 - Steven Bluen
2
什么!!!你试过这个吗?事实恰恰相反。提问者要求的是正在运行的Python脚本的名称 - 而不是当前执行的Python文件。想象一下,当出现错误时,有一个脚本会将脚本名称和允许的参数一起打印出来。您将其放在一个函数中,使用其中一种技术。在某些时候,您决定将该函数移动到外部库中。您想要打印正在运行的主要脚本的名称还是执行的库文件的名称? - John Deighan
3
当您想要打印使用信息时,这是正确的解决方案,因为这正是程序的调用方式,而__file__则不是。 - puravidaso
显示剩余4条评论

154
为了完整起见,我认为总结各种可能的结果并提供每个结果的确切行为的参考是值得的。
答案由四部分组成:
1. 不同方法的列表,返回当前执行脚本的完整路径。 2. 关于处理相对路径的注意事项。 3. 建议处理符号链接的方法。 4. 一些可以用来从完整文件路径中提取实际文件名(带或不带后缀)的方法。

提取完整文件路径

  • __file__ is the currently executing file, as detailed in the official documentation:

    __file__ is the pathname of the file from which the module was loaded, if it was loaded from a file. The __file__ attribute may be missing for certain types of modules, such as C modules that are statically linked into the interpreter; for extension modules loaded dynamically from a shared library, it is the pathname of the shared library file.

    From Python3.4 onwards, per issue 18416, __file__ is always an absolute path, unless the currently executing file is a script that has been executed directly (not via the interpreter with the -m command line option) using a relative path.

  • __main__.__file__ (requires importing __main__) simply accesses the aforementioned __file__ attribute of the main module, e.g. of the script that was invoked from the command line.

    From Python3.9 onwards, per issue 20443, the __file__ attribute of the __main__ module became an absolute path, rather than a relative path.

  • sys.argv[0] (requires importing sys) is the script name that was invoked from the command line, and might be an absolute path, as detailed in the official documentation:

    argv[0] is the script name (it is operating system dependent whether this is a full pathname or not). If the command was executed using the -c command line option to the interpreter, argv[0] is set to the string '-c'. If no script name was passed to the Python interpreter, argv[0] is the empty string.

    As mentioned in another answer to this question, Python scripts that were converted into stand-alone executable programs via tools such as py2exe or PyInstaller might not display the desired result when using this approach (i.e. sys.argv[0] would hold the name of the executable rather than the name of the main Python file within that executable).

  • If none of the aforementioned options seem to work, probably due to an atypical execution process or an irregular import operation, the inspect module might prove useful, as suggested in another answer to this question:

    import inspect
    source_file_path = inspect.getfile(inspect.currentframe())
    

    However, inspect.currentframe() would raise an exception when running in an implementation without Python stack frame.

    Note that inspect.getfile(...) is preferred over inspect.getsourcefile(...) because the latter raises a TypeError exception when it can determine only a binary file, not the corresponding source file (see also this answer to another question).

  • From Python3.6 onwards, and as detailed in another answer to this question, it's possible to install an external open source library, lib_programname, which is tailored to provide a complete solution to this problem.

    This library iterates through all of the approaches listed above until a valid path is returned. If all of them fail, it raises an exception. It also tries to address various pitfalls, such as invocations via the pytest framework or the pydoc module.

    import lib_programname
    # this returns the fully resolved path to the launched python program
    path_to_program = lib_programname.get_path_executed_script()  # type: pathlib.Path
    

处理相对路径

当处理返回相对路径的方法时,可能会尝试调用各种路径操作函数,例如 os.path.abspath(...)os.path.realpath(...) 来提取完整或真实路径。

然而,这些方法依赖于当前路径以推导出完整路径。因此,如果程序先更改了当前工作目录,例如通过 os.chdir(...),然后再调用这些方法,它们将返回一个不正确的路径。


处理符号链接

如果当前脚本是一个符号链接,则所有上述内容都将返回符号链接的路径而不是实际文件的路径,应该调用os.path.realpath(...)来提取后者。


进一步操作以提取实际文件名

os.path.basename(...) 可以应用于上述任何一个对象,以提取实际文件名,os.path.splitext(...) 可以应用于实际文件名以截断其后缀,例如 os.path.splitext(os.path.basename(...))

Python 3.4版本开始,根据PEP 428的规定,pathlib模块PurePath也可用于以上任何情况。具体而言,pathlib.PurePath(...).name提取实际文件名,pathlib.PurePath(...).stem提取没有后缀的实际文件名。


69
请注意,__file__会给出此代码所在的文件,该文件可以被导入并且与正在解释的主文件不同。要获取主文件,可以使用特殊的__main__模块:
import __main__ as main
print(main.__file__)

注意在Python 2.7中__main__.__file__是可行的,但在3.2中不行,因此请使用上述导入语法来实现可移植性。

这在许多情况下都有效,但当我使用来自R语言的rPython包时就不行了。那一定是一个特殊情况,太难处理了。 - Leonid
事实上,rPython包嵌入了Python解释器,这意味着没有像Python独立运行时那样的“主”文件(无论何时嵌入Python都会出现相同的行为)。它在内部导入__main__,用于在RPython之间传递变量,因此在调用任何其他内容之前设置__main__.__file__将是相对容易的,但我甚至不确定在这种情况下什么是适当的值。 - Perkins
这在像iPython这样的交互式终端中不起作用。我得到以下结果:import main as main print(main.file) Traceback (most recent call last): Input In [150] in <cell line: 2> print(main.file)AttributeError: module 'main'没有属性'file'。 - Rich Lysakowski PhD

58
以上答案不错。但我发现使用上述结果更有效率。 这会导致实际的脚本文件名而不是路径。
import sys    
import os    
file_name =  os.path.basename(sys.argv[0])

2
我喜欢分离扩展名,所以我使用: os.path.splitext(os.path.basename(sys.argv[0]))[0] - RufusVS
几年后,当我使用Python 3.8时,它将打印ipykernel_launcher.py而不是文件名。 - Thomas
你正在使用Jupyter Notebook吗? - Manoj Sahu

37

对于现代 Python 版本(3.4+),Path(__file__).name 应该更符合习惯用法。此外,Path(__file__).stem 可以让你获得脚本名称但不包括 .py 扩展名。


NameError: 名称“Path”未定义 - RufusVS
13
你应该先使用 from pathlib import Path - Emil Melnikov
“modern” 意味着 Python 3.x 吗? - einpoklum
2
@einpoklum pathlib 在 Python 3.4 中被引入,因此它应该从 Python 3.4 开始就可以使用。 - Andrey Semakin
1
这段代码抛出了一个错误:Path(file).name Traceback (most recent call last): Input In [155] in <cell line: 1> Path(file).nameNameError: name 'file' is not defined - Rich Lysakowski PhD
它是__file__而不是file - Fermi-4

12

如果你正在进行一次不寻常的导入(例如,它是一个选项文件),请尝试:

import inspect
print (inspect.getfile(inspect.currentframe()))

请注意,这将返回文件的绝对路径。


当在 Platform.io 构建系统中从 env.SConscript 调用当前文件时,以下内容最终有效。 - Adarsha

12

从Python 3.5开始,你可以简单地执行以下操作:

from pathlib import Path
Path(__file__).stem

更多信息请参见:https://docs.python.org/3.5/library/pathlib.html#pathlib.PurePath.stem

例如,我在用户目录下有一个名为test.py的文件,其内容如下:

from pathlib import Path

print(Path(__file__).stem)
print(__file__)

运行此命令的输出结果为:

>>> python3.6 test.py
test
test.py

11

试试这个:

print __file__

这需要上下文。它会抛出一个错误: print(file) Traceback (most recent call last): Input In [156] in <cell line: 1> print(file)NameError: name 'file'未定义。 - Rich Lysakowski PhD

8
你可以不导入os或其他库来实现此操作。
如果你想获取当前Python脚本的路径,请使用:__file__ 如果你只想获取没有.py扩展名的文件名,请使用以下代码:
__file__.rsplit("/", 1)[1].split('.')[0]

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