我相信你肯定遇到过这样的报错
Traceback (most recent call last):
File "main.py", line 549, in <module>
os.chdir(os.path.dirname(__file__))
FileNotFoundError: [Errno 2] No such file or directory: ''
这还要从python的路径问题说起
首先我们了解一下几个重要的概念
这里的 Desktop
指的是运行路径
寻找python 脚本的方式是相对路径寻找
__file__
变量值为 path_test/test.py
知道这些我下面具体说 运行路径不同 会导致的问题
运行路径不同导致的问题
举一个简单的例子
在这个项目中有一个 1.txt
里面包含 Hello world
字符串, 这段程序就是简单的寻找然后并输出,正常是可以输出的像这样在 sublime 中运行,得到结果:
而我们以另一种方式运行:
这样就会提示找不到这个文件,为什么会有这样的问题?
这里出现这种状况的原因主要是因为运行路径不同,而寻找相对路径的 1.txt
文件也就不同。
第一种寻找的 1.txt
真正路径是在 /home/wshuo/Desktop/path_test/1.txt
而第二种寻找的 1.txt
的真正路径是在 /home/wshuo/Desktop/1.txt
这当然会找不到了
这是因为 sublime 运行脚本的时候首先会将脚本存在的路径作为运行路径, 而第二种在终端运行的时候,会把当前所在的路径作为运行路径(我所在的是 Desktop)
下面我简单写一个例子来证明以上两点说法:
os.getcwd()
这个路径可以获取到当前的路径(也就是程序运行的路径)
运行路径不同找不到文件的解决方案
- 第一种你可以把 修改
txt
文本文件改为绝对路径,显然这不是好的解决办法,因为代码不一定存放在什么地方,写成绝对路径寻找txt
文件绝对是很傻的解决方案 - 更改运行的路径, 无论在什么路径运行都更改到项目路径下运行,这样无论 整个项目存放在什么地方都可以找到
txt
文件了
那么如何更改我们的运行路径呢?
我猜大部分都是这样写的
import os
print(__file__)
path = os.path.dirname(__file__)
print(path)
os.chdir(path)
print(os.getcwd())
with open('1.txt') as f:
s = f.read()
print(s)
__file__
的意思是 寻找这个脚本的路径+这个脚本文件的名字(可以是绝对路径寻找或相对路径寻找)
os.path.dirname
这个函数可以取出文件所在的路径
os.chardir
更改运行路径,下面我再使用这两种方式运行这段代码
所以上面代码的作用就可以解决 无论运行路径是否在脚本所在的路径都可以更改为脚本所在的路径
可以看到就算运行路径不在项目路径也不会报错了
更改运行路径导致的其他问题
但是问题真的就解决了吗?
下面我们换一种运行方式来运行
可以看到,更改完的代码进入项目路径运行反而会出错,而我们也可以看到 __file__
变为了 test.py
这里报错是因为 __file__
没有路径了完全就是一个文件名字了,所以肯定就获取不到他的父级目录了,而这段代码为什么在 sublime 运行的时候没有报错? 不应该他们运行的路径都是项目路径吗?
我们来看另一种运行方式(可能看到这里你已经懵逼了,但是其实我后面会做一个总结看完总结在看这些一下子就清晰明了了)
可以看到这里我们又没有报错了,因为这里我们寻找这个脚本文件是绝对路径寻找的!
前面我一直再说运行路径 不同导致的问题,这里引入一个新的概念:寻找脚本的方式 绝对路径寻找 和相对路径寻找
python运行方式分类
运行媒介 | 运行路径 | 寻找脚本的方式 |
ide 运行 | 文件所在路径 | 绝对路径寻找 |
双击脚本文件 | 文件所在路径 | 绝对路径寻找 |
cmd 或终端运行 | 不确定,看当前所在的路径 | 绝对路径寻找或相对路径寻找 |
可以看到 在终端或 cmd 中运行脚本的时候是最灵活的了,而我们可以以相对路径寻找和绝对路径寻找运行脚本, 运行路径也可以随着当前所在的路径不同而不同,所以用os.chdir
上面修改过的代码可以在
运行路径 | 寻找脚本的方式 |
任意 | 至少有一级父级目录的相对路径寻找或直接的绝对路径寻找 |
这种条件下运行成功,因为如果进入项目路径下运行的话就必须有至少一层父级路径可以提供,否则获取不到父级路径,也就更改不了运行路径(报错是在获取不到父级目的地方报错)
知道报错原因,你也可以在项目路径这样运行脚本
wshuo@wshuo-Aspire-F5-572G:~/Desktop/path_test$ python3 ../path_test/test.py
Hello world!
这样也不会报错,就是退到上级目录然后在进入项目目录运行,这样就可以获取到文件所在的目录了,但是很傻,我们又不能确定用户会不会在文件所在目录运行这个脚本,更不会确定用户会不会以这样的方式运行脚本
最终的解决办法
- 做一个简单的容错处理
import os
try:
os.chdir(os.path.dirname(__file__))
except:
pass
with open('1.txt') as f:
s = f.read()
print(s)
这种解决方案肯定包含了所有情况,因为只有当 运行路径为 项目路径时 且python 后面只有 test.py 这个文件名的时候 才会报错,但在这种情况下根本不需要考虑更改运行路径的问题, 因为运行路径已经是 项目路径了,肯定是可以找到 1.txt
文件的
- 用
os.path.abspath
处理一下再获取脚本所在路径
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
with open('1.txt') as f:
s = f.read()
print(s)
这种解决方案也是不管 __file__
这个变量是只是一个文件名经过获取绝对路径都可以变成绝对路径+文件名
的形式, 这样再获取脚本所在的路径肯定也不会报错的