和LEGB那个坑一样,最终解决办法都是寻找编译器对程序的解释顺序,脚本语言这玩意,基本捋顺了这个问题,遇到的问题就解决一大半了。
那么开始,我在要运行的run.py中引入A.py和B.py两个文件,但是B.py中要引入A.py的一个函数foo。于是至此我必须在B.py中再次引入A.py。这我就懵逼了,我是在run.py中运行,而我同时引入A.py和B.py拉呀。我对于引入的理解是,编译器不就是把A.py和B.py复制进run.py吗?为什么还要再B.py中多引入一次呢?其实编辑器的引入方式和我想的有所不同,稍微复杂也就造成不能那么运行。
2)循环嵌套
例如:
文件[A.py]
from B import D
class C:pass
文件[B.py]
from A import C
class D:pass
为什么执行 A 的时候不能加载 D 呢?
如果将 A.py 改为:import B 就可以了,这是怎么回事呢?
这跟Python内部 import 的机制是有关的,具体到 from B import D,Python 内部会分成几个步骤:
1)在 sys.modules 中查找符号 “B”
2)如果符号 B 存在,则获得符号 B 对应的 module 对象。
从 的 __dict__ 中获得符号 “D” 对应的对象,如果 “D” 不存在,则抛出异常。
3)如果符号 B 不存在,则创建一个新的 module 对象 ,注意,此时,module 对象的 __dict__ 为空。
执行 B.py 中的表达式,填充 的 __dict__。
从 的 __dict__ 中获得 “D” 对应的对象,如果 “D” 不存在,则抛出异常。
所以这个例子的执行顺序如下:
1、执行 A.py 中的 from B import D 由于是执行的 python A.py,所以在 sys.modules 中并没有 存在, 首先为 B.py 创建一个 module 对象 () ,注意,这时创建的这个 module 对象是空的,里边啥也没有,在 Python 内部创建了这个 module 对象之后,就会解析执行 B.py,其目的是填充 这个 __dict__。
2、执行 B.py中的from A import C 在执行B.py的过程中,会碰到这一句, 首先检查sys.modules这个module缓存中是否已经存在, 由于这时缓存还没有缓存, 所以类似的,Python内部会为A.py创建一个module对象(), 然后,同样地,执行A.py中的语句
3、再次执行A.py中的from B import D 这时,由于在第1步时,创建的对象已经缓存在了sys.modules中, 所以直接就得到了, 但是注意,从整个过程来看,我们知道,这时还是一个空的对象,里面啥也没有, 所以从这个module中获得符号”D”的操作就会抛出异常。 如果这里只是import B,由于”B”这个符号在sys.modules中已经存在,所以是不会抛出异常的。
我们看他在解决循环嵌套的问题时,提到python解释器的机制是,先执行A.py然后为B.py创建一个对象module,然后解释B.py,并让B.py填充这个module!!!所以他是会解释B.py的,而不是把B的代码直接复杂进A,所以B自身也要五脏俱全。引入他不存在的函数他会报错。