python模块相互导入时报错AttributeError: partially initialized module module-name has no attribute-name most

C++的#include类似于宏替换,.cpp最初的编译就会将#include的文件进行替换操作,此时如果发生相互包含会产生无穷递归报错。同样dll之间的相互依赖(比如项目A与项目B相互依赖),当在项目A新增export的函数或类并让项目B去import项目A新增的那项目或类时,可以先注释掉项目B中import项目A的代码并生成两个项目(两者中总需要有一个是从最初不依赖对方的状态生成的dll开始一路生成),此时生成的项目A.dll中已经有了新增的export的函数或类,然后解开项目B中import项目A的代码并生成项目B,就可以链接到项目A中新增的函数或类了。

当然,相互依赖是愚蠢的,不能丢掉最初不依赖对方的那个dll,否则相互依赖是无解的。

然后说一下我试验出来的主调模块导入被调模块时"并不是等被调模块执行完,才在主调模块中创建模块变量与对应模块对象,而是首先创建模块变量(毕竟只是个引用),然后一边执行被调模块一边生成被调模块对象,一边生成被调模块变量的名字空间(module-name.attributes),在完全执行完被调模块之前生成的被调模块对象被称为"partially initialized module"部分初始化模块,以及由此导致的两种相互导入时产生的现象:

1、相互导入并调用的属性在调用前定义,既使循环导入,也能顺利执行

2、相互导入并调用的属性在调用后定义,此时循环导入将找不到属性

3、相互导入并调用的属性在调用前定义,将其中一方的导入放到其函数或方法中,也能顺利执行

         文Python 3.x可能是史上最详解的【导入(import)】给出的"解决这种circular import循环导入的方法"方法1所述"延迟导入(lazy import)":把import语句卸载方法/函数里,将它的作用域限制在局部(此法可能导致性能问题)讲的就是这个意思。

        得益于python的编译机制(名字空间与模块对象在运行时创建,从而允许使用"部分初始化模块对象partially initialized moudle"中已存在的名字),可以通过"相互导入并调用的属性在调用前定义"巧妙地避免找不到属性的错误

4、相互导入并调用的属性在调用后定义,将其中一方的导入放在其函数或方法中,此时循环导入将找不到属性

        文https://blog.csdn.net/weixin_38256474/article/details/81228492给出的"解决这种circular import循环导入的方法"方法2将from x import y改成import x.y隐含的原理是”from x import y”事实上首先将整体import x从而创建临时模块对象x,这将执行x.py中的global域执行语句(函数与类定义不执行),然后再在主调模块创建global变量y并指向临时模块对象x中的属性y,最后销毁临时模块变量x,从而禁止用户使用x.y访问模块对象的其他属性,并不是字面意思上只导入指定的属性!

        至于"import x.y",没明白这是什么意思,"python3.8 -m 包.模块"+"from 包.模块 import 名字"与"from 模块 import 名字"的结果是一样的,要想相互导入能顺利执行都得要"相互导入并调用的属性在调用前定义"

猜你喜欢

转载自blog.csdn.net/HayPinF/article/details/107453474