__file__ 变量的含义/作用是什么?
- 2024-12-17 08:30:00
- admin 原创
- 140
问题描述:
import os
A = os.path.join(os.path.dirname(__file__), '..')
B = os.path.dirname(os.path.realpath(__file__))
C = os.path.abspath(os.path.dirname(__file__))
我通常只是将这些与实际路径硬连接起来。但这些语句在运行时确定路径是有原因的,我真的很想了解该os.path
模块,以便我可以开始使用它。
解决方案 1:
当从 Python 中的文件加载模块时,__file__
设置为其绝对路径。然后,您可以将其与其他函数一起使用来查找文件所在的目录。
一次举一个例子:
A = os.path.join(os.path.dirname(__file__), '..')
# A is the parent directory of the directory where program resides.
B = os.path.dirname(os.path.realpath(__file__))
# B is the canonicalised (?) directory where the program resides.
C = os.path.abspath(os.path.dirname(__file__))
# C is the absolute path of the directory where the program resides.
您可以在此处看到返回的各种值:
import os
print(__file__)
print(os.path.join(os.path.dirname(__file__), '..'))
print(os.path.dirname(os.path.realpath(__file__)))
print(os.path.abspath(os.path.dirname(__file__)))
并确保从不同的位置(例如./text.py
,~/python/text.py
等等)运行它,以查看会产生什么差异。
解决方案 2:
我只是想先解决一些困惑。 __file__
不是通配符,而是属性。双下划线属性和方法按照惯例被视为“特殊”的,并具有特殊用途。
http://docs.python.org/reference/datamodel.html显示了许多特殊方法和属性,如果不是全部的话。
在这种情况下__file__
是模块(模块对象)的属性。在 Python 中,.py
文件就是模块。因此,在不同情况下,属性import amodule
的含义会有所不同。__file__
摘自文档:
__file__
如果模块是从文件加载的,则为加载模块的文件的路径名。__file__
对于静态链接到解释器的 C 模块,不存在此属性;对于从共享库动态加载的扩展模块,它是共享库文件的路径名。
在您的情况下,模块正在访问__file__
全局命名空间中它自己的属性。
要查看实际效果,请尝试:
# file: test.py
print globals()
print __file__
然后运行:
python test.py
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__file__':
'test_print__file__.py', '__doc__': None, '__package__': None}
test_print__file__.py
解决方案 3:
根据文件:
__file__
如果模块是从文件加载的,则为加载模块的文件的路径名。__file__
对于静态链接到解释器的 C 模块,不存在此属性;对于从共享库动态加载的扩展模块,它是共享库文件的路径名。
还有:
__file__
是文件的“路径”,除非该模块是内置的(因此列在中sys.builtin_module_names
),在这种情况下未设置该属性。
解决方案 4:
这里只想简单说明一下(主要是回答问题的标题而不是描述),关于一个可能会让一些人感到困惑的变化。从 Python 3.4 开始,行为方式发生了轻微变化__file__
:
如果直接执行该模块,则将其设置为使用该模块的相对路径。
否则,它被设置为文件的绝对路径。
模块
__file__
属性(及相关值)现在应始终默认包含绝对路径,唯一的例外是__main__.__file__
直接使用相对路径执行脚本的情况。(由 Brett Cannon 在问题 18416中贡献。)
例子:
直接调用模块 x 并间接调用模块 y:
# 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__))
运行python3 x.py
会输出:
/home/aderchox/mytest/y.py
/home/aderchox/mytest/y.py
x.py
x.py
/home/aderchox/mytest/x.py
解决方案 5:
__file__
与各种模块结合使用os.path
可使所有路径与当前模块的目录位置相关。这可使您的模块/项目可移植到其他机器。
在您的项目中您执行以下操作:
A = '/Users/myname/Projects/mydevproject/somefile.txt'
然后尝试使用部署目录将其部署到您的服务器,/home/web/mydevproject/
那么您的代码将无法正确找到路径。
解决方案 6:
为了补充 aderchox 的答案,__file__
变量的行为在 Python 3.9 中再次发生了变化,现在它在所有情况下都是绝对路径
运行相同的示例(但为了保持一致性,将其复制到此处)
# 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__))
现在运行x.py
两个不同版本的解释器
$ python3.8 x.py
/private/tmp/y.py
/private/tmp/y.py
x.py
x.py
/private/tmp/x.py
$ python3.9 x.py
/private/tmp/y.py
/private/tmp/y.py
/private/tmp/x.py
/private/tmp/x.py
/private/tmp/x.py
来源:
https ://docs.python.org/3/whatsnew/3.9.html#other-language-changes
解决方案 7:
亡灵法术
[仅在 PYTHON 3.7 中测试]
我使用:
os.sep.join(__file__.split(os.sep)[:-1])
与我见过的其他解决方案相比,我更喜欢它。我写了一些测试代码:
from timeit import Timer
from typing import List, Tuple
N = 100000
TIMEITOUT = [None]
def _p_timeit(stmt: str, setup: str = ""):
tte = Timer(f"TIMEITOUT[0]={stmt}", setup=f"{setup};from __main__ import TIMEITOUT").timeit(N)
print(f"{stmt:<54}|{tte:>17.10f} [output]: \"{TIMEITOUT[0]}\"")
return stmt, tte
def _p_header():
print(f"Testing w/ number={N} iterations
{'=' * 72}")
print(f"{'Statement':<54}|{'Time':^17}
{'-' * 72}")
def _p_compare(a: Tuple[str, float], b_l: List[Tuple[str, float]]):
print(f"
{'=' * 72}
Comparing {a[0]} to all other statements:
{'-' * 72}")
for b in b_l:
d = (b[1]-a[1])/a[1]
cmp = f"faster than" if d > 0 else f"slower than" if d < 0 else f"equally (t={a[1]}) as fast as"
print(f"{a[0]} was {(abs(d)*100):.2f}% {cmp} {b[0]}")
_p_header()
my_suggestion = _p_timeit("os.sep.join(__file__.split(os.sep)[:-1])", setup="import os")
others = [_p_timeit("os.path.abspath(os.path.dirname(__file__))", setup="import os"),
_p_timeit("Path(__file__).parent", setup="from pathlib import Path"),
_p_timeit("Path(__file__).resolve().parent", setup="from pathlib import Path")]
_p_compare(my_suggestion, others)
输出:
Testing w/ number=100000 iterations
========================================================================
Statement | Time
------------------------------------------------------------------------
os.sep.join(__file__.split(os.sep)[:-1]) | 0.0640765000 [output]: "C:UsersabrewerAppDataLocalProgramsPythonPython37lib"
os.path.abspath(os.path.dirname(__file__)) | 0.6156060000 [output]: "C:UsersabrewerAppDataLocalProgramsPythonPython37lib"
Path(__file__).parent | 0.7117664000 [output]: "C:UsersabrewerAppDataLocalProgramsPythonPython37lib"
Path(__file__).resolve().parent | 9.3563913000 [output]: "C:UsersabrewerAppDataLocalProgramsPythonPython37Lib"
========================================================================
Comparing os.sep.join(__file__.split(os.sep)[:-1]) to all other statements:
------------------------------------------------------------------------
os.sep.join(__file__.split(os.sep)[:-1]) was 860.74% faster than os.path.abspath(os.path.dirname(__file__))
os.sep.join(__file__.split(os.sep)[:-1]) was 1010.81% faster than Path(__file__).parent
os.sep.join(__file__.split(os.sep)[:-1]) was 14501.91% faster than Path(__file__).resolve().parent
请记住,如果 file 返回的字符串使用除 os.sep 之外的任何分隔符(例如:os.altsep),程序将失败。我还没有遇到过这种情况,但谁知道呢……
如果需要,您可以先在字符串中找到分隔符,然后使用它代替 os.sep。根据时间复杂度标准(查找分隔符的时间基本不变),速度改进将保持不变。
解决方案 8:
使用 pathlib.Path 处理此问题的最简单、“pythonic”的方法是将其用作:
ex1 = Path(__file__).parent/"myfilename.xyz"
ex2 = Path(__file__).parent/"whatever_folder/myfilename.xyz"
ex3 = Path(__file__).parent/"../whatever_folder/myfilename.xyz"
因为你可以直接使用“/”。
扫码咨询,免费领取项目管理大礼包!