模块的 __file__ 属性是绝对路径还是相对路径?

130 投票
5 回答
137064 浏览
提问于 2025-04-16 23:55

我对 __file__ 有点困惑。根据我的理解,__file__ 会返回加载这个模块时的绝对路径。

我遇到的问题是这样的:我有一个 abc.py 文件,里面有一句话 print __file__。当我在 /d/projects/ 目录下运行 python abc.py 时,它返回的是 abc.py。但是当我在 /d/ 目录下运行时,它返回的是 projects/abc.py。这是为什么呢?

5 个回答

21

这里有一个简单的例子:

from os import path, getcwd, chdir

def print_my_path():
    print('cwd:     {}'.format(getcwd()))
    print('__file__:{}'.format(__file__))
    print('abspath: {}'.format(path.abspath(__file__)))

print_my_path()

chdir('..')

print_my_path()

在Python 2.*版本中,第二次调用会错误地根据当前目录来判断path.abspath(__file__)的值:

cwd:     C:\codes\py
__file__:cwd_mayhem.py
abspath: C:\codes\py\cwd_mayhem.py
cwd:     C:\codes
__file__:cwd_mayhem.py
abspath: C:\codes\cwd_mayhem.py

正如@techtonik所提到的,在Python 3.4及以上版本中,这个问题就不存在了,因为__file__会返回一个绝对路径。

63

__file__ 从 Python 3.4 开始默认是绝对路径,除了当你直接用相对路径执行一个脚本的时候:

模块的 __file__ 属性(以及相关的值)现在默认应该总是包含绝对路径,唯一的例外是当一个脚本直接用相对路径执行时,__main__.__file__ 会是相对路径。(这个改动是由 Brett Cannon 提出的,详细信息可以查看 bpo-18416。)

不过我不确定它是否会解析符号链接。

下面是一个使用相对路径的例子:

$ python script.py
105

__file__ 在 Python 3.9 及以上版本中保证是一个绝对路径。

在 Python 3.4 的更新说明中提到,(更新日志)

模块的 __file__ 属性(以及相关值)现在默认应该总是包含绝对路径,唯一的例外是当脚本直接使用相对路径执行时的 __main__.__file__

在 Python 3.9 的更新说明中提到,(更新日志)

... __main__ 模块的 __file__ 属性变成了绝对路径。


根据 文档

如果模块是从文件加载的,那么 __file__ 属性就是加载该模块的文件的路径。对于某些类型的模块,比如静态链接到解释器的 C 模块,__file__ 属性可能不存在。对于从共享库动态加载的扩展模块,它是共享库文件的路径。

在 @kindall 的评论中提到的 邮件列表讨论中提到:

我没有尝试重现这个特定的例子,但原因是我们不想在每次导入时都调用 getpwd(),也不想有某种进程内变量来缓存当前目录。(getpwd() 相对较慢,有时可能会失败,而尝试缓存它也有一定的错误风险。)

我们所做的是在 site.py 中编写代码,遍历 sys.path 中的元素并将它们转换为绝对路径。不过这段代码在 '' 被插入到 sys.path 的最前面之前就运行了,所以 sys.path 的初始值是 ''。

接下来,考虑 sys.path 不包含 ''

所以,如果你在不包含模块的 sys.path 部分外部,你会得到一个绝对路径。 如果你在包含模块的 sys.path 部分内部,你会得到一个相对路径

如果你在当前目录加载一个模块,而当前目录不在 sys.path 中,你会得到一个绝对路径。

如果你在当前目录加载一个模块,而当前目录 sys.path 中,你会得到一个相对路径。

撰写回答