如何在Python中获取当前执行文件的路径?


193

这似乎是一个新手问题,但事实并非如此。一些通用方法并非在所有情况下都有效:

sys.argv [0]

这意味着使用path = os.path.abspath(os.path.dirname(sys.argv[0])),但是如果您是从另一个目录中的另一个Python脚本运行的,则此方法不起作用,并且这可能在现实生活中发生。

__文件__

这意味着使用path = os.path.abspath(os.path.dirname(__file__)),但是我发现这不起作用:

  • py2exe没有__file__属性,但是有一种解决方法
  • 当您从IDLE运行时,execute()没有__file__属性
  • 我得到的OS X 10.6 NameError: global name '__file__' is not defined

答案不完整的相关问题:

我正在寻找一种通用解决方案,该解决方案可以在所有上述用例中使用。

更新资料

这是一个测试用例的结果:

python a.py的输出(在Windows上)

a.py: __file__= a.py
a.py: os.getcwd()= C:\zzz

b.py: sys.argv[0]= a.py
b.py: __file__= a.py
b.py: os.getcwd()= C:\zzz

py

#! /usr/bin/env python
import os, sys

print "a.py: sys.argv[0]=", sys.argv[0]
print "a.py: __file__=", __file__
print "a.py: os.getcwd()=", os.getcwd()
print

execfile("subdir/b.py")

subdir / b.py

#! /usr/bin/env python
import os, sys

print "b.py: sys.argv[0]=", sys.argv[0]
print "b.py: __file__=", __file__
print "b.py: os.getcwd()=", os.getcwd()
print

C:.
|   a.py
\---subdir
        b.py

Answers:


80

您无法直接确定正在执行的主脚本的位置。毕竟,有时脚本根本不是来自文件。例如,它可能来自交互式解释器或仅存储在内存中的动态生成的代码。

但是,由于总是从文件加载模块,因此您可以可靠地确定模块的位置。如果使用以下代码创建模块并将其与主脚本放在同一目录中,则主脚本可以导入模块并使用该模块定位自身。

some_path / module_locator.py:

def we_are_frozen():
    # All of the modules are built-in to the interpreter, e.g., by py2exe
    return hasattr(sys, "frozen")

def module_path():
    encoding = sys.getfilesystemencoding()
    if we_are_frozen():
        return os.path.dirname(unicode(sys.executable, encoding))
    return os.path.dirname(unicode(__file__, encoding))

some_path / main.py:

import module_locator
my_path = module_locator.module_path()

如果您在不同目录中有多个主脚本,则可能需要一个以上的module_locator副本。

当然,如果您的主脚本是由其他工具加载的,而这些工具却不允许您导入与脚本位于同一位置的模块,那么您很不走运。在这种情况下,您所需要的信息根本就不在程序中任何地方。最好的选择是向工具作者提交错误。


2
我提到在OS 10.6上我正在NameError: global name '__file__' is not defined使用文件,但这不在IDLE内。认为__file__仅在模块内部定义。
sorin

1
@Sorin Sbarnea:我更新了如何解决这个问题的答案。
丹尼尔·斯图兹巴赫

2
谢谢,但是实际上缺少的问题__file__与Unicode无关。我不知道为什么__file__未定义,但是我正在寻找一种通用的解决方案,该解决方案将在所有情况下均适用。
sorin

1
抱歉,并非在所有情况下都可行。例如,我尝试从wscript文件(python)内部的waf.googlecode.com中执行此操作。这些文件已执行,但不是模块,您不能将它们设置为模块(它们可以是源树中的任何子目录)。
sorin 2010年

1
那不会给你位置some_path/module_locator.py吗?
Casebash13年

68

首先,您需要从inspect和导入os

from inspect import getsourcefile
from os.path import abspath

接下来,只要您要在哪里使用它就可以找到源文件

abspath(getsourcefile(lambda:0))

最佳答案。谢谢。
Devan Williams

4
好答案。谢谢。它似乎也是最短,最可移植的(在不同的os-es上运行)答案,并且不会遇到诸如NameError: global name '__file__' is not defined(另一种解决方案导致此问题)的问题。
爱德华

同样简短的另一种可能性:lambda:_。它对我有用-不确定是否会一直有效。lambda:0可能运行的速度快得不可估量(或者可能不是那么小...可能是立即加载,或者对于0全局加载而言甚至更快_?)是否更干净,更易于阅读,或更聪明/晦涩,这值得商bat。
ArtOfWarfare

与我尝试过的几乎所有建议一样,这只会为我返回cwd,而不是我正在运行的目录文件(调试)。
詹姆斯

1
@James-此代码在您正在运行的文件中...运行getsourcefile(lambda:0)将毫无意义,None如果您尝试在交互式提示符下运行它,则返回该代码(因为lambda它将不在任何源文件中。)如果您想知道在交互环境中来自其他功能或对象的地方,也许abspath(getsourcefile(thatFunctionOrObject))对您有帮助?
ArtOfWarfare 18/09/23

16

该解决方案即使在可执行文件中也很强大

import inspect, os.path

filename = inspect.getframeinfo(inspect.currentframe()).filename
path     = os.path.dirname(os.path.abspath(filename))

2
这应该是正确的答案。即使在中也可以使用entry_point: console_script,但没有其他答案。
波兰

15

我遇到了类似的问题,我认为这可能可以解决问题:

def module_path(local_function):
   ''' returns the module path without the use of __file__.  Requires a function defined
   locally in the module.
   from http://stackoverflow.com/questions/729583/getting-file-path-of-imported-module'''
   return os.path.abspath(inspect.getsourcefile(local_function))

它适用于常规脚本并处于空闲状态。我只能说是为他人尝试!

我的典型用法:

from toolbox import module_path
def main():
   pass # Do stuff

global __modpath__
__modpath__ = module_path(main)

现在,我使用__modpath__而不是__file__。


2
根据PEP8编码样式指南,永远不要创建带有双下划线和下划线的名称,因此__modpath__应重命名。您也可能不需要该global语句。否则+1!
martineau 2012年

4
实际上,您可以在调用中定义本地函数module_path()。即module_path(lambda _: None),它不依赖于脚本所在的其他内容
。– martineau

@martineau:我接受了您的建议,lambda _: None并在过去的两年中使用了它,但是现在我发现我可以将其压缩为just lambda:0。您是否有任何特殊原因建议您使用忽略了参数_而不是完全没有参数的形式?在None前缀前面加上空格而不只是上面有什么优点0吗?我认为它们都一样神秘,只有一个是8个字符长,而另一个是14个字符长。
ArtOfWarfare 2015年

@ArtOfWarfare:两者之间的区别在于,lambda _:该函数接受一个参数,lambda:而该函数不接受任何参数。没关系,因为永远不会调用该函数。同样,使用什么返回值也没关系。我猜我之所以选择None它,是因为当时它似乎表明它是一个无所事事,永不被称为的函数。它前面的空间是可选的,这里也是为了提高可读性(总是尝试遵循PEP8是习惯的习惯)。
martineau

@martineau:显然这是对它的滥用lambda,将它用于从来没有做过的事情。如果您要遵循PEP8,我认为正确的内容应该是pass,而不是None,但是在中放置语句是无效的lambda,因此您必须输入带有值的内容。您可以输入一些有效的2个字符,但是我认为您可以输入的唯一有效的单个字符是0-9(或在lambda。之外分配的单个字符变量名称)。我认为0最好的表示零- 9。
ArtOfWarfare 2015年

6

简短的答案是,无法保证获得所需信息的方法,但是在实践中,启发式方法几乎总是起作用。您可能会看看如何在C中找到可执行文件的位置?。它从C的角度讨论了该问题,但是提出的解决方案很容易被转录为Python。


你好,我的标志,但被拒绝,所以我现在已经开始在元的讨论:meta.stackoverflow.com/questions/277272/...
ArtOfWarfare

但是,此页面上已经有一些简单的工作答案。我的解决方案效果很好:stackoverflow.com/a/33531619/3787376
爱德华

5

请参阅我对从父文件夹导入模块问题的回答,以获取相关信息,包括为什么我的答案不使用不可靠的__file__变量。这个简单的解决方案应与作为模块的不同操作系统交叉兼容,osinspect作为Python的一部分。

首先,您需要导入inspectos模块的一部分。

from inspect import getsourcefile
from os.path import abspath

接下来,在Python代码中其他需要的地方使用以下行:

abspath(getsourcefile(lambda:0))

这个怎么运作:

从内置模块os(如下所述)abspath中导入工具。

Mac,NT或Posix的OS例程,取决于我们所使用的系统。

然后getsourcefile(从下面的描述)从内置模块导入inspect

从实时Python对象获取有用的信息。

  • abspath(path) 返回文件路径的绝对/完整版本
  • getsourcefile(lambda:0)以某种方式获取lambda函数对象的内部源文件,因此'<pyshell#nn>'在Python shell中返回或返回当前正在执行的Python代码的文件路径。

使用abspath的结果getsourcefile(lambda:0)应确保生成的文件路径是Python文件的完整文件路径。
这个解释好的解决方案最初是基于我如何在Python中获取当前执行文件路径的答案中的代码的?


是的,我只是想出了相同的解决方案……比仅仅说无法可靠完成的答案要好得多……除非问题不是问我认为是什么...
Grady Player

5

您只是简单地打电话给:

path = os.path.abspath(os.path.dirname(sys.argv[0]))

代替:

path = os.path.dirname(os.path.abspath(sys.argv[0]))

abspath()为您提供sys.argv[0](代码所在的文件名)的绝对路径,并dirname()返回不包含文件名的目录路径。


3

这应该以跨平台的方式来解决问题(只要您不使用解释器之类):

import os, sys
non_symbolic=os.path.realpath(sys.argv[0])
program_filepath=os.path.join(sys.path[0], os.path.basename(non_symbolic))

sys.path[0]是您的调用脚本所在的目录(它首先查找该脚本要使用的模块)。我们可以将文件本身的名称从结尾删除sys.argv[0](这是我所做的os.path.basename)。os.path.join只是以跨平台的方式将它们粘在一起。os.path.realpath只要确保我们得到的符号链接名称与脚本本身的名称不同,就仍能获得脚本的真实名称。

我没有Mac;因此,我尚未对此进行测试。请让我知道它是否有效,好像应该起作用。我在Linux(Xubuntu)和Python 3.4上对此进行了测试。请注意,许多解决此问题的方法在Mac上不起作用(因为我听说__file__Mac上不存在此解决方案)。

请注意,如果脚本是符号链接,它将为您提供链接到的文件的路径(而不是符号链接的路径)。


2

您可以Pathpathlib模块中使用:

from pathlib import Path

# ...

Path(__file__)

您可以使用call进行parent进一步操作:

Path(__file__).parent

正如StackOverflow用户经常提到的,此答案使用的__file__变量可能不可靠(并不总是完整的文件路径,不适用于所有操作系统等)。将答案更改为不包括答案将减少问题,并且更加兼容。有关更多信息,请参见stackoverflow.com/a/33532002/3787376
爱德华

@ mrroot5好,所以请删除您的评论。
加夫里耶尔·科恩

1

如果代码来自文件,则可以获取其全名

sys._getframe().f_code.co_filename

您也可以将函数名称检索为 f_code.co_name


0

只需添加以下内容:

from sys import *
path_to_current_file = sys.argv[0]
print(path_to_current_file)

要么:

from sys import *
print(sys.argv[0])

0

我的解决方案是:

import os
print(os.path.dirname(os.path.abspath(__file__)))

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.