Python从父目录的其他文件夹中导入模块的解决方案(亲测有效)
简单吐槽
这两天在下载github上的python程序来跑的时候,发现经常出现的一个问题:当在某一python脚本文件想要导入父目录下的其他文件夹中的模块时,会报错Traceback (most recent call last):
File "/home/zhuangb/桌面/pp/test2/p2.py", line 15, in <module>from test1.p1 import *
ModuleNotFoundError: No module named 'test1'
太难受了。于是乎在网上找各种答案,几乎都是同一种解决方法,也不介绍方法的原理是怎样的。所以我一尝试这个方法用不了,其他博客的解决方法也是用不了。就有种感觉,我算数算到1+1=4,然后网上的博客清一色的1+1=3,又不给出1+1=3是怎么算出来的,结果这个答案是错的,你同样也不知道错在哪。最恼人的就是找了半天,发现答案都一样,真是一言难尽。
接下来正式提出问题。
遇到的问题
假设文件的目录结构是这样的:
-pp
| - test1
| | | - __init__.py
| | | - p1.py
| - test2
| | | - p2.py
对了,这里提一句,如果要想一个文件夹下的文件作为模块被引用的话,__init__.py
文件是必须的。有了__init__.py
这个文件,导入时系统就会认为这个文件夹下的脚本文件都是可以被引用的模块,而不是普通的文件。
现在在p2.py
中,我想要importtest1
目录下的p2.py
中的一个hello()方法,也就是导入父目录的其他文件夹下的模块,一般代码都是这么写:
from test1.p1 import *
hello()
-------------------------------------------------------------------------------
Traceback (most recent call last):
File "/home/zhuangb/桌面/pp/test2/p2.py", line 14, in <module>
from test1.p1 import *
ModuleNotFoundError: No module named 'test1'
报错。
常见答案
(有些情况下确实有效,有效的原因我后面会分析,但是在我的系统测试出错):
import sys
sys.path.append("..")
from test1.p1 import *
hello()
----------------------------------------------------------------------------------
Traceback (most recent call last):
File "/home/zhuangb/桌面/pp/test2/p2.py", line 13, in <module>
from test1.p1 import *
ModuleNotFoundError: No module named 'test1'
报错。
不过关于这个方法,提供了一个解决方法的思路,那就是sys.path
。在程序导包的过程中,会遍历这个变量中的所有路径来寻找需要的包。让我们看看执行sys.path.append("..")
后sys.path
中都有啥:
import sys
sys.path.append("..")
print(sys.path)
----------------------------------------------------------------
['/home/zhuangb/桌面/pp/test2',
'/usr/lib/python36.zip',
'/usr/lib/python3.6',
'/usr/lib/python3.6/lib-dynload',
'/usr/lib/python3/dist-packages',
'..']
看到了原始的'/home/zhuangb/桌面/pp/test2'
,以及我们添加的'..'
。很明显在'/home/zhuangb/桌面/pp/test2'
肯定找不到test1,更别说test1下的文件。让我们看看'..'
代表的地址是什么。经过我的测试,应该就是当前的工作路径加上'..'
,也就是当前工作路径的父目录。所以问题的关键就在于,后面添加的这个'..'
路径,如果在这个路径下能够看到test1文件夹,那就必然能够顺着test1找到文件夹中的文件。
import os
import sys
sys.path.append("..")
# 获取当前的工作路径
print(os.getcwd())
------------------------------------------------------------------
/home/zhuangb/桌面/pp
根据上面的结果,就能够发现为什么使用sys.path.append("..")
在sys.path
中添加'..'
会导包失败了。我也不知道为什么当前工作路径是pp
(也就是test1和test2的父目录)而不是pp/test2
。
所以实际上,sys.path.append("..")
就是在向sys.path
添加/home/zhuangb/桌面
,也就是pp
的父目录。在这个目录下,只能看得到pp
这个文件夹,根本看不到pp
里面有test1
和test2
,因此找不到test1
下的p1.py
就很正常了。
正确的解决方案
所以最正确的方法,应该是分析在你运行的时候,你的当前工作路径是什么。根据你的工作路径,向sys.path
添加相应的路径,让系统在这个路径中去寻找你想要导的包。在我的这个情况下,我的当前工作路径是/home/zhuangb/桌面/pp
,test1
就在这个路径下。所以我只要将/home/zhuangb/桌面/pp
添加到sys.path
中,也就是只要我添加一个'.'
,表示添加当前工作路径至sys.path
中,就可以了。系统就会顺着pp
找到下面的test1
文件夹,也就可以找到test1
中的p1.py
。
import os
import sys
# 添加当前工作路径/home/zhuangb/桌面/pp(test1就在这个路径下)至sys.path中
# 系统就可以找到/home/zhuangb/桌面/pp/test1/p1.py
# 实测,以下三种方案都可以
sys.path.append(os.getcwd())
# 或者
# sys.path.append(".")
# 或者
# sys.path.append(os.path.realpath("."))
print(sys.path)
from test1.p1 import *
# p1.py中的hello()方法
hello()
--------------------------------------------------------
['/home/zhuangb/桌面/pp/test2',
'/usr/local/lib/python3.6/dist-packages',
'/usr/lib/python3/dist-packages',
'/home/zhuangb/桌面/pp']
hello world.
成功!!!
总结
python导包时会在sys.path
中的所有路径下去寻找。因此,如果找不到相应的包,只要往sys.path
中添加正确的包所在的路径,就必然可以导入。问题解决!!
就这个小问题,居然花了我一上午的时间,气死了!网上找的答案基本都是一模一样的,一个不行就全部都不行。
有什么问题可以在评论中提出来,我尽我所能为你们解答。
下一篇: 鸿蒙桌面卡片怎么调节大小?