【问题标题】:Is module __file__ attribute absolute or relative?模块 __file__ 属性是绝对的还是相对的?
【发布时间】:2011-10-30 07:42:22
【问题描述】:

我无法理解__file__。据我了解,__file__ 返回加载模块的绝对路径。

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

【问题讨论】:

标签: python


【解决方案1】:

__file__ 是绝对的since Python 3.4,除非直接使用相对路径执行脚本:

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

不确定它是否可以解析符号链接。

传递相对路径的例子:

$ python script.py

【讨论】:

  • 谢谢。这是一个很难追查的事实!
  • 这不适用于 Python 3.4.0 (Python 3.4.0 (default, Apr 11 2014, 13:05:11) [GCC 4.8.2] on linux)。在我的试验中,符号链接没有得到解决。
  • @FrozenFlame,如果 3.4.1 没有修复它,请随时向 bugs.python.org 报告。
  • os.path.realpath(__file__) 是解析符号链接的正确方法吗?
【解决方案2】:

来自documentation

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

来自@kindall 在对问题的评论中链接的mailing list thread

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

我们所做的是在 site.py 中遍历元素的代码 sys.path 并将它们转换为绝对路径。但是此代码运行 before '' 被插入到 sys.path 的前面,这样初始 sys.path 的值为 ''。

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

因此,如果您不在包含该模块的sys.path 部分,您将获得绝对路径。如果您位于包含模块的 sys.path 部分内,您将获得相对路径

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

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

【讨论】:

  • 这是否意味着如果存在从 '' 到模块的路径,则将使用相对路径,如果不是绝对路径,则将使用绝对路径,因为 sys.path 的其余部分是绝对的。 .
  • 如果你在当前目录加载一个模块,而当前目录不是sys.path,你会得到一个绝对路径。如果你在当前目录中加载一个模块,并且当前目录sys.path中,你会得到一个相对路径。
  • 请记住,为此目的,sys.path 不包括 ''
  • 知道了,但是@agf,如果我使用 /home 中的 python /foo/abc.py,我想 sys.path 中包含模块的部分是 /home/foo 和我的当前目录是/home/,为什么打印文件给我一个相对路径?
【解决方案3】:

后期简单示例:

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

【讨论】:

  • ... __main__ 模块除外,其中__file__ 可能是相对路径。
【解决方案4】:

借助@kindall 提供的Guido 邮件,我们可以将标准导入过程理解为尝试在sys.path 的每个成员中查找模块,并将此查找的结果文件(更多细节在@ 987654321@.)。因此,如果模块位于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 中时,如果你添加/tmp 你的PYTHONPATH 你会得到:

>>> 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() 相对较慢,有时会彻底失败,

所以你的路径被原样使用

【讨论】:

    猜你喜欢
    • 2011-11-21
    • 2011-07-19
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 2010-12-29
    • 2019-09-09
    • 2015-10-11
    • 2015-11-13
    相关资源
    最近更新 更多