Is module __file__ attribute absolute or relative?

我很难理解 __file__。据我所知,__file__返回加载模块的绝对路径。

我在生成这个函数时遇到了问题: 我有一个带有一个语句 print __file__abc.py,从 /d/projects/ python abc.py运行返回 abc.py。从 /d/运行返回 projects/abc.py。有什么原因吗?

131162 次浏览

来自 文件:

如果模块是从文件加载的,则 __file__是加载该模块的文件的路径名。对于静态链接到解释器的 C 模块,__file__属性不存在; 对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

由@kindall 链接的 邮件列表线程在评论中提出了这样一个问题:

我没有试图复制这个特殊的例子,但原因是 我们不希望对每个导入都调用 getpwd () ,我们也不希望 want to have some kind of in-process variable to cache the current (getpwd ()相对较慢,有时会失败 并且试图缓存它有一定的错误风险。)

我们所做的是在 site.py 中遍历 并将它们转换为绝对路径 将“ before”插入到 sys.path 的前面,以便初始化 值为”。

对于其余部分,请考虑 sys.path不包括 ''

因此,如果您在包含模块的 sys.path部分之外,您将得到一个 绝对路径。如果您位于包含该模块的 sys.path部分中,那么您将得到一个 相对路径

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

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

借助@kindall 提供的 Guido mail,我们可以将标准导入过程理解为尝试在 sys.path的每个成员中查找模块,并将其作为查找的结果(更多细节见 PyMOTW Modules and Imports).因此,如果模块位于 sys.path中的绝对路径中,结果是绝对的,但如果模块位于 sys.path中的相对路径中,则结果是相对的。

现在,site.py启动文件只负责在 sys.path中传递绝对路径(除了初始的 '') ,所以如果你不通过其他方式改变它,而是设置 PYTHONPATH (在 sys.path前缀之前,它的路径也变成绝对路径) ,你将总是得到一个绝对路径,但是当模块通过工作目录访问时。

现在,如果你以一种有趣的方式欺骗 sys.path,你可以得到任何东西。

例如,如果在 /tmp/中有一个样例模块 foo.py,其代码如下:

import sys
print(sys.path)
print (__file__)

如果你进入/tmp,你会得到:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
./foo.py

/home/user中,如果你把 /tmpPYTHONPATH相加,你会得到:

>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
/tmp/foo.py

即使添加 ../../tmp,它也会被规范化,结果也是一样的。

但是,如果不使用 PYTHONPATH,你直接使用一些有趣的路径 你得到的结果和原因一样有趣。

>>> import sys
>>> sys.path.append('../../tmp')
>>> import foo
['', '/usr/lib/python3.3', .... , '../../tmp']
../../tmp/foo.py

Guido 在上面引用的帖子中解释了为什么 python 不尝试在绝对路径中转换所有条目:

我们不希望在每次导入时都调用 getpwd () ... ..。 Getpwd ()相对较慢,有时会彻底失败,

所以使用的路径是 就是这样

除非直接使用相对路径执行脚本,否则 __file__是绝对 从 Python 3.4开始:

默认情况下,模块 __file__属性(以及相关值)现在应该始终包含绝对路径,当使用相对路径直接执行脚本时,__main__.__file__是唯一的例外。(由 Brett Cannon 在 Bpo-18416提供)

但不确定它是否解析了符号链接。

传递相对路径的示例:

$ python script.py

最后一个简单的例子:

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__返回一个绝对路径。

__file__可以是相对的,也可以是绝对的,这取决于所使用的 Python 版本以及模块是否直接执行。

译者:

  • 在 python 3.5 to 3.8中,如果模块被直接调用,那么 __file__被设置为模块 w.r.t 的相对路径,即当前工作目录。否则它被设置为绝对路径。
  • 在 python3.9及3.10中,即使直接执行相应的模块,也将 __file__设置为绝对路径。

例如,具有以下设置(受此 回答的启发) :

# x.py:
from pathlib import Path
import y
print(__file__)
print(Path(__file__))
print(Path(__file__).resolve())


# y.py:
from pathlib import Path
print(__file__)
print(Path(__file__))

with x.py and y.py in the same directory. The different outputs after going to this directory and executing:

python x.py

是:

Python 3.5-3.8:

D:\py_tests\y.py
D:\py_tests\y.py
x.py
x.py
D:\py_tests\x.py

Python 3.9-3.10

D:\py_tests\y.py
D:\py_tests\y.py
D:\py_tests\x.py
D:\py_tests\x.py
D:\py_tests\x.py

注意: 此测试已在 Windows10中完成