现象
做项目的时候,一直使用 os.path.dirname(os.path.abspath(__file__)) 来获取当前目录。然而,最近却遇到了一个路径相关的问题。直接运行 py 文件是正常的,但是打包成 exe 之后,却显示因为路径问题导致程序报错无法继续执行。
比如我有一段获取当前目录的脚本代码:
test.py
import os
import timedef get_current_dir():return os.path.dirname(os.path.abspath(__file__))if __name__ == '__main__':current_dir = get_current_dir()print(current_dir) # E:\lky_project\tmp_project\test_projecttime.sleep(10)
正常用 Python 解释器执行的话输出 E:\lky_project\tmp_project\test_project,看起来没什么问题。
好,接下来,我用下面的命令对 test.py 打包,打包后将生成的 test.exe 移动到和 test.py 相同的目录下(确保执行目录一致)。
E:\lky_project\tmp_project\test_project> pyinstaller -F ./test.py
然后双击运行 test.exe,结果输出的目录却是下面这种(看起来是个临时目录)。
显然,这两个输出不是同一个目录,而我中间只是做了打包的操作,并没有对代码进行任何额外修改,但为什么输出的目录却不一样呢?
原因
我搜了一下 DeepSeek,其中的分析我还是比较认同的,所以就不多说了。分析的原因如下:
- __file__ 变量通常是指当前执行脚本的路径。当直接运行脚本时,__file__ 会返回该脚本的文件名,然后通过 os.path.abspath 获取绝对路径,再取目录名,得到的就是脚本所在的目录路径。这应该是正确的。
- 但是当打包成 exe 后,比如使用 PyInstaller,情况可能会不同。PyInstaller 打包后的 exe 文件会将脚本解压到一个临时目录中运行,这时候 __file__ 可能指向的是这个临时目录中的路径,而不是原来的脚本位置。这就会导致 current_dir 在打包后得到的是临时目录的路径,而不是用户期望的 exe 所在的目录。
解决
既然原因找到了,总得找个解决方案。
sys.frozen
其中一个解决方法,利用程序打包前和打包后 sys.frozen 环境变量的不同来进行区分,不同情况使用不同的目录获取方式。
import sys
import os
import timedef get_current_dir():if getattr(sys, 'frozen', False):# 打包后的情况,使用 sys.executable 的目录print("frozen")return os.path.dirname(sys.executable)else:# 打包前,正常脚本执行print("not frozen")return os.path.dirname(os.path.abspath(__file__))current_dir = get_current_dir()
print(current_dir)time.sleep(10)
os.getcwd()
再有就是使用 os.getcwd() 来获取当前目录。
import os
import timedef get_current_dir():return os.path.abspath(os.getcwd())current_dir = get_current_dir()
print(current_dir)time.sleep(10)
pathlib
或者使用 pathlib 的 absolute() 或 resolve() 方法也可以。
import time
import pathlibdef get_current_dir():# return pathlib.Path("./").resolve()return pathlib.Path("./").absolute()current_dir = get_current_dir()
print(current_dir)time.sleep(10)