我理解起来很困难__file__.据我所知,__file__返回加载模块的绝对路径.
我遇到问题:我有abc.py一个声明print __file__,从/d/projects/ python abc.py返回运行abc.py.从/d/退货运行projects/abc.py.有什么原因吗?
agf*_*agf 94
从文档:
__file__是加载模块的文件的路径名,如果它是从文件加载的.__file__对于静态链接到解释器的C模块,该属性不存在; 对于从共享库动态加载的扩展模块,它是共享库文件的路径名.
从@kindall在评论中链接的邮件列表线程到问题:
我没有尝试重新编写这个特定的例子,但原因是我们不想在每次导入时调用getpwd(),也不希望有某种进程内变量来缓存当前目录.(getpwd()相对较慢,有时可能会彻底失败,并且尝试缓存它有一定的错误风险.)
我们所做的是site.py中的代码,它遍历sys.path的元素并将它们转换为绝对路径.但是,此代码在''插入sys.path前面之前运行,因此sys.path的初始值为''.
对于其余部分,请考虑sys.path不包括''.
所以,如果你在sys.path那个包含模块的部分之外,你将得到一个绝对的路径.如果你在sys.path包含模块的部分内,你将得到一个相对路径.
如果在当前目录中加载模块,并且当前目录不在,则将sys.path获得绝对路径.
如果在当前目录中加载一个模块,当前目录是在sys.path,你会得到一个相对路径.
ana*_*nik 54
__file__自Python 3.4以来是绝对的,除非直接使用相对路径执行脚本:
现在,模块
__file__属性(和相关值)默认情况下应始终包含绝对路径,唯一的例外是__main__.__file__使用相对路径直接执行脚本的时间.(由Brett Cannon在bpo-18416中提供.)
不确定它是否能解析符号链接.
传递相对路径的示例:
$ python script.py
Run Code Online (Sandbox Code Playgroud)
Sim*_*AsG 14
后期简单的例子:
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()
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
正如@techtonik所指出的,在Python 3.4+中,这将很好地工作,因为它__file__返回一个绝对路径.
__file__可以是相对的或绝对的,具体取决于使用的 python 版本以及模块是否直接执行。
长话短说:
__file__如果直接调用模块,则设置为模块相对于当前工作目录的相对路径。否则它被设置为绝对路径。__file__即使直接执行相应的模块,也设置为绝对路径。Python 3.9 的新增功能中解释了此行为(感谢@user0 的评论):
Python 现在获取在命令行上指定的脚本文件名的绝对路径(例如:)
python3 script.py:模块__file__的属性__main__变成了绝对路径,而不是相对路径。现在,在 更改当前目录后,这些路径仍然有效os.chdir()。作为副作用,__main__在这种情况下,回溯还会显示模块帧的绝对路径。
玩具示例: 例如,具有以下设置(受此答案启发):
# x.py:
from pathlib import Path
import y
print(__file__)
print(Path(__file__))
print(Path(__file__).resolve())
Run Code Online (Sandbox Code Playgroud)
# y.py:
from pathlib import Path
print(__file__)
print(Path(__file__))
Run Code Online (Sandbox Code Playgroud)
与x.py和y.py在同一目录中。进入该目录并执行后的不同输出:
python x.py
Run Code Online (Sandbox Code Playgroud)
是:
Python 3.5 - 3.8:
D:\py_tests\y.py
D:\py_tests\y.py
x.py
x.py
D:\py_tests\x.py
Run Code Online (Sandbox Code Playgroud)
Python 3.9 - 3.11
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
Run Code Online (Sandbox Code Playgroud)
借助@kindall 提供的 Guido 邮件,我们可以将标准导入过程理解为尝试在 的每个成员中查找模块sys.path,并将文件作为此查找的结果(更多详细信息参见PyMOTW 模块和导入。)。所以如果模块位于绝对路径中sys.path结果是绝对的,但如果它位于相对路径中sys.path结果是相对的。
现在site.py启动文件只负责传递 中的绝对路径sys.path,除了初始'',所以如果你不通过设置 PYTHONPATH(其路径也被设为绝对路径,在前缀之前sys.path)之外的其他方式更改它,你将始终获得绝对路径路径,但是当模块通过当前目录访问时。
现在,如果你以一种有趣的方式欺骗 sys.path 你可以得到任何东西。
作为例子,如果你有一个样品模块foo.py中/tmp/的代码:
import sys
print(sys.path)
print (__file__)
Run Code Online (Sandbox Code Playgroud)
如果你进入 /tmp 你会得到:
>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
./foo.py
Run Code Online (Sandbox Code Playgroud)
在 in 中/home/user,如果你添加/tmp你的,PYTHONPATH你会得到:
>>> import foo
['', '/tmp', '/usr/lib/python3.3', ...]
/tmp/foo.py
Run Code Online (Sandbox Code Playgroud)
即使添加了../../tmp,也会被归一化,结果是一样的。
但是,如果不是PYTHONPATH直接使用一些有趣的路径,您会得到与原因一样有趣的结果。
>>> import sys
>>> sys.path.append('../../tmp')
>>> import foo
['', '/usr/lib/python3.3', .... , '../../tmp']
../../tmp/foo.py
Run Code Online (Sandbox Code Playgroud)
Guido 在上面引用的线程中解释了为什么 python 不尝试转换绝对路径中的所有条目:
我们不想在每次导入时都调用 getpwd() .... getpwd() 相对较慢,有时会完全失败,
因此,您的路径按原样使用。