浅析Python模块之间的相互引用问题


Posted in Python onFebruary 26, 2021

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

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

报错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(相对引用)的介绍

浅析Python模块之间的相互引用问题

通俗一点意思就是,你程序入口运行的那个模块,就默认为主模块,他的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_main执行时,他被当做根目录,因此他引用的src.src_test1 是绝对路径,这样引用到哪都不会错,此时他的name=‘main',当执行src_test1的时候,注意了此时test1的name是 src.src_test1,那么在test1中使用的是相对路径,查找逻辑是先找到父节点(src目录),再找父节点下面的src_test2,因此可以成功找到,Bingo!

辅证:

构造一个例子,就可以理解上面的 执行目录就是根目录 的说法了,修改test1,使引用test_main:

from .. import test_main
 
报错:ValueError: attempted relative import beyond top-level package

OK,那继续构造第二个报错:

上文中说过,解决main 的问题,就是创建一个模块,来调用使用相对路径的模块,那么为什么我不能在相同目录下创建这个文件来调用呢?让我们来测试下代码:

创建test_src.py文件,代码结构变更如下:

|--- test_main.py
|--- src
 |--- __init__.py                               
  |--- src_test1.py
  |--- src_test2.pys
  |--- test_src.py

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,os
sys.path.append(os.getcwd())
from src.src_test2 import Test2

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

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

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

到此这篇关于Python模块之间的相互引用问题的文章就介绍到这了,更多相关Python模块引用内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Django imgareaselect手动剪切头像实现方法
May 26 Python
基于Python实现对PDF文件的OCR识别
Aug 05 Python
Windows下Python2与Python3两个版本共存的方法详解
Feb 12 Python
python之PyMongo使用总结
May 26 Python
pandas 取出表中一列数据所有的值并转换为array类型的方法
Apr 11 Python
python实现图片彩色转化为素描
Jan 15 Python
python批量爬取下载抖音视频
Jun 17 Python
python对文件目录的操作方法实例总结
Jun 24 Python
django的auth认证,authenticate和装饰器功能详解
Jul 25 Python
简单了解Java Netty Reactor三种线程模型
Apr 26 Python
python爬虫数据保存到mongoDB的实例方法
Jul 28 Python
pycharm 的Structure界面设置操作
Feb 05 Python
python实现学生通讯录管理系统
Feb 25 #Python
基于Pytorch版yolov5的滑块验证码破解思路详解
Feb 25 #Python
python实现不同数据库间数据同步功能
Feb 25 #Python
使用python实现学生信息管理系统
Feb 25 #Python
pytho matplotlib工具栏源码探析一之禁用工具栏、默认工具栏和工具栏管理器三种模式的差异
Feb 25 #Python
使用tkinter实现三子棋游戏
Feb 25 #Python
python matplotlib工具栏源码探析二之添加、删除内置工具项的案例
Feb 25 #Python
You might like
php安全配置 如何配置使其更安全
2011/12/16 PHP
php类常量的使用详解
2013/06/08 PHP
PHP生成自适应大小的缩略图类及使用方法分享
2014/05/06 PHP
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
用jquery实现输入框获取焦点消失文字
2013/04/27 Javascript
js触发select onchange事件的小技巧
2014/08/05 Javascript
jquery实现类似淘宝星星评分功能实例
2014/09/12 Javascript
JavaScript中的类(Class)详细介绍
2014/12/30 Javascript
JS实现的N多简单无缝滚动代码(包含图文效果)
2015/11/06 Javascript
仿iframe效果Aajx文件上传实例
2016/11/18 Javascript
Javascript使用function创建类的两种方法(推荐)
2016/11/19 Javascript
Vue.js 2.0窥探之Virtual DOM到底是什么?
2017/02/10 Javascript
MUI 解决动态列表页图片懒加载再次加载不成功的bug问题
2017/04/13 Javascript
JavaScript实现QQ聊天消息展示和评论提交功能
2017/05/22 Javascript
浅谈实现vue2.0响应式的基本思路
2018/02/13 Javascript
js单线程的本质 Event Loop解析
2019/10/29 Javascript
js实现经典贪吃蛇小游戏
2020/03/19 Javascript
vue项目中自定义video视频控制条的实现代码
2020/04/26 Javascript
VUE 单页面使用 echart 窗口变化时的用法
2020/07/30 Javascript
理解JavaScript中的对象
2020/08/25 Javascript
[48:35]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 TNC vs Optic
2018/04/03 DOTA
Python版的文曲星猜数字游戏代码
2013/09/02 Python
Python3调用微信企业号API发送文本消息代码示例
2017/11/10 Python
解读! Python在人工智能中的作用
2017/11/14 Python
python opencv之SIFT算法示例
2018/02/24 Python
python 文件转成16进制数组的实例
2018/07/09 Python
pandas 将索引值相加的方法
2018/11/15 Python
Python及Pycharm安装方法图文教程
2019/08/05 Python
python 无损批量压缩图片(支持保留图片信息)的示例
2020/09/22 Python
Anaconda详细安装步骤图文教程
2020/11/12 Python
Vision Directa智利眼镜网:框架眼镜、隐形眼镜和名牌太阳眼镜
2016/11/23 全球购物
阿联酋电子产品购物网站:Menakart
2017/09/15 全球购物
澳大利亚玩具剧场:Toy Playhouse
2019/03/03 全球购物
物业管理计划书
2014/01/10 职场文书
2014年环保工作总结
2014/11/26 职场文书
Python 类,对象,数据分类,函数参数传递详解
2021/09/25 Python