__file__ 不保证是绝对路径,因其取决于模块导入方式;应统一用 pathlib.Path(__file__).resolve() 获取可靠绝对路径,避免相对路径引发的 FileNotFoundError。

为什么 __file__ 有时不是绝对路径?
__file__ 是模块被加载时 Python 自动设置的属性,但它**不保证是绝对路径**——如果模块是用相对路径导入(比如在 shell 中执行 python ./my_script.py),__file__ 就会是相对路径。直接拼接或 os.path.abspath(__file__) 虽然能补救,但不如一开始就用 pathlib 处理干净。
关键点:不要假设 __file__ 可直接用于文件操作,尤其在打包(如 PyInstaller)或跨平台部署时,相对路径会导致 FileNotFoundError。
用 pathlib.Path(__file__).resolve() 获取可靠绝对路径
resolve() 是最稳妥的方式:它自动展开符号链接、补全相对路径、处理 .. 和 .,且跨平台行为一致(Windows 下也返回带盘符的路径)。
- 在普通脚本中:
Path(__file__).resolve()返回该脚本自身的绝对路径(PosixPath或WindowsPath) - 在包内模块中(如
my_pkg/utils.py):同样有效,得到的是该模块文件的绝对路径,不是包根目录 - 如果只需要目录,链式调用
.parent:Path(__file__).resolve().parent
示例:
from pathlib import Path print(Path(__file__).resolve()) # /home/user/project/src/main.py print(Path(__file__).resolve().parent) # /home/user/project/src
获取包根目录不能只靠 __file__
很多人误以为 Path(__file__).resolve().parent.parent 就是包根目录,但这是硬编码层级,极易出错——比如包结构从 src/my_pkg/ 变成 my_pkg/,代码就挂了。
更健壮的做法是结合 importlib.resources(Python 3.9+)或 importlib_metadata(旧版本),或者用 Path(__file__).resolve().parent 配合明确的标记文件(如 pyproject.toml)向上搜索:
- 搜索上层最近的
pyproject.toml:Path(__file__).resolve().parents[0].joinpath("pyproject.toml").exists(),再用itertools.takewhile往上找 - 若项目已安装为可导入包,可用
importlib.resources.files("my_pkg")(推荐) - 避免用
__file__拼出“项目根”,除非你完全控制部署方式
打包后(PyInstaller / cx_Freeze)的 __file__ 行为差异
打包工具会把模块解压到临时目录(如 /tmp/_MEIxxx/),此时 __file__ 指向的是运行时解压路径,不是原始源码路径。这本身没问题,但要注意:
- 如果你在代码里写死了
../data/config.json这种相对路径,打包后可能找不到——因为工作目录不等于解压目录 - 正确做法:一律基于
Path(__file__).resolve().parent构建资源路径,而不是依赖os.getcwd() - PyInstaller 提供
sys._MEIPASS,但优先用Path(__file__).resolve().parent,它在开发和打包环境下都有效
真正容易被忽略的是:路径逻辑一旦混入 os.getcwd() 或 os.chdir(),就会在打包后彻底失效——__file__ 和当前工作目录根本不在一个地方。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











