ImportError: cannot import name ‘XXX‘ 问题最全解最根本决方案 [循环引用]

0. 前言

导入的实质是什么? 以下是我从简书收到的一篇文章(https://www.jianshu.com/p/a1e91cc53b07), 我截了一个片段:参考自https://www.jianshu.com/p/a1e91cc53b07
python中,每个py文件被称之为模块,每个具有__init__.py文件的目录被称为包.只要模
块或者包所在的目录在sys.path中,就可以使用import 模块或import 包来使用.可以通过以下方式查看导入路径.

>>> print(sys.path)
['D:\\PyCharm\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pydev', 'D:\\PyCharm\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pycharm_display', 'D:\\PyCharm\\PyCharm 2020.1.1\\plugins\\python\\helpers\\third_party\\thriftpy', 'D:\\PyCharm\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pydev', 'F:\\代码\\flask_test_venv\\Scripts\\python37.zip', 'd:\\anaconda\\DLLs', 'd:\\anaconda\\lib', 'd:\\anaconda', 'F:\\代码\\flask_test_venv', 'F:\\代码\\flask_test_venv\\lib\\site-packages', 'D:\\PyCharm\\PyCharm 2020.1.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend', 'F:\\代码\\flask_test_venv', 'F:/代码/flask_test_venv']

1. 问题原因之一: 从现有包中导入不存在名称
例如:

>>> from flask import notexist
Traceback (most recent call last):
  File "<input>", line 1, in <module>
ImportError: cannot import name 'notexist' from 'flask' (F:\代码\flask_test_venv\lib\site-packages\flask\__init__.py)

由于"notexist"这个名称根本不存在于包flask中, 所以会导致导入失败.
解决方式: 检查命名.

2. 问题原因之二: 自定义包覆盖python原有包
例如: 我们自己定义了一个包叫做http, 而我们的工程中用到了requests这个包, 那我们import requests 可能会出现这个错误, 因为requests模块会试图从http包中导入相关名称, 但是这个http包使我们自己定义的, 其中不包含requests引用的(官方http包中的)名称.

https://blog.csdn.net/u012206617/article/details/101292699
有很多相同问题博客, 上边就是一个例子.
应该是学习七月老师的python flask实战课程遇到的问题.

解决方式: 将自定义包改名, 比如myhttp.

3. 问题原因之三: 循环引用导致导入失败
这个问题是最棘手的, 问题三的解决方式有很多, 网络上也都给出了答案.比如可以采取:

  1. 使用延迟导入(lazy import)
  2. 直接导入模块名,通过模块调用其中的函数

以上两种解决方案博客https://www.jianshu.com/p/a1e91cc53b07中都描述了问题和解决方案, 或者网络上搜索"循环引用"也能搜到很多类似解决方案.

但是对于最最根本的解决方案都是一句带过:重新设计代码结构.是的, 重新设计代码结构可以解决问题, 重新设计的思路是什么样的? 俗话说授人以鱼不如授人以渔, 下面我给大家介绍一种我自己总结的方法, 就像上高中数学老师经常介绍的做题窍门, 学会了就能又快又好地解决问题啦.
3. 重新设计代码结构,将代码和并或者分离
所需材料: 一张纸, 一支笔. 我们先将引用关系用笔画在纸上, 看看循环引用到底是怎么形成的.
规定一下:
框框 中是文件名, 框框下边 是这个文件中的变量名.箭头 代表引用关系: 被引用指向引用.如果是文件引用的文件, 就画 框框 指向 框框 ; 如果是文件引用变量名, 就画 框框下的变量名 指向 框框 .具体看下边:

  1. 画出引用结构
    在这里插入图片描述
    怎么查找循环引用? 可以从Python的报错信息出发, 从头到尾梳理引用关系, 这一步还是挺简单的.然后将引用关系画出来. 如果单靠脑子想象, 那你可能浪费一两天的时间, 但是问题还是没有解决. 或者说解决了你也不清楚是怎么解决的.一支笔, 一张纸, 20分钟就能将问题梳理明确.
    可以看到app.moudels.user中的User被引用, 同时其中又引用app.init中的login_manager, 形成了一个引用闭环, 即循环引用.
  2. 调整引用顺序
    在这里插入图片描述
    怎么解决上述循环引用? 我们可以看到主要问题出现在login_manager上, 那么我们就从它入手, 调整其位置, 解除循环引用关系. 当我们把login_manager的定义从app.init中移动到app.models.user中后, 我们可以看到循环引用已经消失, 程序也可以正常运行了.
    6. 总结
    循环引用, 顾名思义就是一个文件1(app.init)引用的其他文件(app.models.user)中, 有对文件1的再次引用(login_manager).
    解决方案也随着引用关系图的梳理变得清晰起来: 我们可以将图中的变量(login_manager)想象成可以移动的东西, 将他在纸上移动, 直到闭环不复存在.
    7. 感想
    在这样画出引用关系之前, 我的感觉是崩溃的. 在脑子里梳理这个引用关系, 我看了两个晚上的时间, 然而是越看越乱, 问题依旧没有解决. 后来我想起之前写dfs算法的时候只靠脑子想, 写出来的算法逻辑有很多漏洞, 画了流程图之后很快就写出一个比较完美的dfs.
    说了这么多, 我想告诉大家的是, 如果感觉单靠脑子里想象解决不了的问题, 请赶紧拿出纸笔, 梳理思维, 这样会事半功倍.
  • 22
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值