python相对模块引入与相对文件路径

0x00 相对路径相对的是哪个路径

首先我们要知道python会参考两个路径分别是cwdpath,其分别可以通过

1
2
3
4
5
import os
cwd = os.getcwd() # cwd 工作路径

import sys
path = sys.path # path 环境变量

我们发现在导入模块和读取文件时相对的是不同路径,这在读取和导入不再同一父目录的文件和模块时尤为重要。

我们有一个project其目录结构如图:

1
2
3
4
5
6
7
8
|-- project
|-- task
|--test1.py
|--test2.py
|--11.txt
|-- test1.py
|-- test2.py
|-- 11.txt

0x01 cwd 工作目录

工作目录是python解释器运行的目录,程序去读取文件会更具cwd来寻找相对地址,且只会根据cwd,而与path无关

1
2
3
4
5
6
# 在project目录下运行py文件
PS D:\tmp\project> python task/test1.py
this is project/task/test1.py, now cwd is D:\tmp\project and sys.path[0] is D:\tmp\project\task
# 在task目录下运行py文件
PS D:\tmp\project\task> python test1.py
this is project/task/test1.py, now cwd is D:\tmp\project\task and sys.path[0] is D:\tmp\project\task
可以看到在不同的目录下使用python解释器运行文件会有不同cwd,而具有相同的sys path

0x02 sys path

sys path第一项是python文件所在的目录,在导入模块时,会去搜索path中的地址和相对地址,且与cwd无关

1
2
3
4
5
6
# 运行project目录下的test
D:\tmp\project> python test1.py
this is project/test1.py, now cwd is D:\tmp\project
# 运行project/task文件下的test
PS D:\tmp\project> python task/test1.py
this is project/test1.py, now cwd is D:\tmp\project and sys.path[0] is D:\tmp\project\task
可以看到sys path的第一项根据python文件进行更改。

0x03 根据相对地址导入模块

导入模块时相对的时sys.path中的地址。 ### 在task/test1.py导入test2.py

在跑一个项目时,看到他在导入同一目录下文件时使用的是以下代码,并且该写法vscode的静态语法检查不会报错。

1
2
3
#file: project/task/test1.py
import task.test2.py

运行结果是
1
2
3
4
5
6
PS D:\tmp\project> & D:/ANACONDA/envs/GA/python.exe d:/tmp/project/task/test1.py
this is project/task/test1.py, now cwd is D:\tmp\project and sys.path[0] is d:\tmp\project\task
Traceback (most recent call last):
File "d:\tmp\project\task\test1.py", line 4, in <module>
import task.test2
ModuleNotFoundError: No module named 'task'

通过sys path第一项我们可以看到其在d:

解决办法

我们需要将父目录添加到环境变量中才能导入父目录这个文件夹。 * 方法1: 将导入修改为

1
import test2.py 
* 方法2: 我们要使我们的环境兼容他的代码 * 在debug使可以在launch中添加父目录
1
2
3
4
5
6
7
8
9
10
11
12
    "version": "0.2.0",
"configurations": [
{
"name": "Python 调试程序: 当前文件",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"env": {"PYTHONPATH":"${workspaceFolder}"},// 工作目录或者".."
"envFile": "${workspaceFolder}/.env"
}
],
* 也可以手动在powershell中为python添加环境变量
1
2
cd ..
$env:PYTHONENV=$pwd

0x04 根据相对地址导入文件

我在网上查找了很多资料,都是人都说相对路径相对的是当前文件的路径,其实不然。从0x01我们知道cwd会随着python调用的改变而改变,即使文件地址不变,在不同目录调用使用python文件会读到不同的文件

实验表明读取文件的相对地址是相对于cwd

test1.py代码:

1
2
3
4
5
import os
import sys
print(f"this is project/task/test1.py, now cwd is {os.getcwd()} and sys.path[0] is {sys.path[0]}")
with open("11.txt", "r") as f:
print(f.read())
在不同目录调用test1的实验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#在project目录中调用project/task/test1
PS D:\tmp\project> & D:/ANACONDA/envs/GA/python.exe d:/tmp/project/task/test1.py
this is project/task/test1.py, now cwd is D:\tmp\project and sys.path[0] is d:\tmp\project\task
i am project/11.txt
#在task目录中调用project/task/test1.py
this is project/task/test1.py, now cwd is D:\tmp\project\task and sys.path[0] is D:\tmp\project\task
i am project/task/11.txt
'''

python 文件修改为open("../11.txt", "r"),
'''
#在task目录中调用project/task/test1.py
PS D:\tmp\project\task> & D:/ANACONDA/envs/GA/python.exe d:/tmp/project/task/test1.py
this is project/task/test1.py, now cwd is D:\tmp\project\task and sys.path[0] is d:\tmp\project\task
i am project/11.txt
#在project目录中调用
this is project/task/test1.py, now cwd is D:\tmp\project and sys.path[0] is D:\tmp\project\task
Traceback (most recent call last):
File "D:\tmp\project\task\test1.py", line 4, in <module>
with open("../11.txt", "r") as f:
FileNotFoundError: [Errno 2] No such file or directory: '../11.txt'
'''
文件会报错,因为在project的父目录,也即D:\tmp中没有11.txt文件。
'''
可以看到path没有变,而cwd变化了,并且读取到了不同的txt文件。所以读取文件的相对路径依据的是cwd


python相对模块引入与相对文件路径
https://jfsas.github.io/2024/10/23/python相对模块引入与相对文件路径/
作者
JFSAS
发布于
2024年10月23日
许可协议