写点什么

终于搞懂了 Python 模块之间的相互引用问题

发布于: 2021 年 02 月 05 日

摘要:详细讲解了相对路径和绝对路径的引用方法。


在某次运行过程中出现了如下两个报错:


报错1: ModuleNotFoundError: No module named '__main__.src_test1'; '__main__' is not a package报错2: ImportError: attempted relative import with no known parent package 
复制代码


于是基于这两个报错探究了一下 python3 中的模块相互引用的问题,下面来逐个解析,请耐心看完。


好的,我们先来构造第一个错:


测试代码结构如下:


|--- test_main.py|--- src  |--- __init__.py                                                                  |--- src_test1.py    |--- src_test2.py
复制代码


src_test2.py 代码


class Test2(object):    def foo(self):        print('I am foo')
复制代码


src_test1.py 代码,引用 Test2 模块


from .src_test2 import Test2
def fun1(): t2 = Test2() t2.foo()if __name__ == "__main__": fun1()
复制代码


此时运行 src_test1.py 报错“No module named '__main__.src_test1'; '__main__' is not a package”


问题原因:


主要在于引用 src_test2 模块的时候,用的是相对路径".",在 import 语法中翻译成"./",也就是当前目录下,按这样理解也没有问题,那为什么报错呢?


从 PEP 328 中,我们找到了关于 the relative imports(相对引用)的介绍


image


通俗一点意思就是,你程序入口运行的那个模块,就默认为主模块,他的 name 就是‘main’,然后会将本模块 import 中的点(.)替换成‘__main__’,那么 .src_test2 就变成了 __main__.src_test2,所以当然找不到这个模块了。


解决方法:


因此,建议的做法是在 src 同层级目录创建 引用模块 test_main.py(为什么不在 src 目录下创建,待会下一个报错再讲),并引用 src_test1 模块,代码如下:


from src.src_test1 import fun1
if __name__ == "__main__": fun1()
test_src 代码:
from src_test1 import fun1
if __name__ == "__main__": fun1()
复制代码


执行报错:ImportError: attempted relative import with no known parent package


问题原因:


当执行 test_src 时,按上文理解,此时执行文件所在的目录为根目录,那么引用 test1 的时候,需要注意的是,此时 test1 的 name 属性不再是 src.src_test1,因为程序感知不到 src 的存在,此时他的绝对路径是 src_test1,此时再次引用相对路径查找的 test2,同样的步骤,需要先找到父节点,而此时他自己就是根节点了,已经没有父节点了,因此报错“no known parent package”。


解决方法:


此时为了避免父节点产生矛盾,因此将 test1 中的引入去掉相对引用即可


from .src_test2 import Test2 --> from src_test2 import Test2
复制代码


继续深入:


那使用相对路径和绝对路径,编译器是怎么找到这个模块的呢?


执行 import 的时候,存在一个引入的顺序,即优先查找执行目录下有没有此文件,如没有,再查找 lib 库下,如还没有,再查找 sys.path 中的路径,如再没有,报错。


所以不管是当前目录,还是 sys.path 中的目录,都可以查到 src_test2 这个模块,就可以编译成功。


号外


解决完上述问题后,不管我们用哪种方式,我们调试代码时,都是单个文件调试,但此时根目录就不对了,import 方式又要改动,执行起来很麻烦,所以这里推荐另一种方式(有更好的方式欢迎留言),使用 sys.path.append()的方法


import sys,ossys.path.append(os.getcwd())from src.src_test2 import Test2
复制代码


使用 append 的方式,将程序文件根目录放进了 sys.path 中,然后再引用绝对路径,这样的方式,不管使用上文中的第一或第二执行方式都可以调用,也可以单独编译 test1 文件,不用修改 import 路径,也是相对安全的方式。但是缺点就是,如果你修改了某一个包名,需要将所有引用地方都修改一下,工作量大,所以因地制宜。


综上,详细讲解了相对路径和绝对路径的引用方法,现在你应该对 import 导入的问题有了清晰的理解吧


备注:本文基于 Python3.7 版本测试


点击关注,第一时间了解华为云新鲜技术~


发布于: 2021 年 02 月 05 日阅读数: 20
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
终于搞懂了Python模块之间的相互引用问题