课后作业P50讲:模块问题
本帖最后由 天下有敌 于 2020-6-22 06:58 编辑见第5题
原题:
5. 执行下边 a.py 或 b.py 任何一个文件,都会报错,请尝试解释一下此现象。
# a.py
from b import y
def x():
print('x')
# b.py
from a import x
def y():
print('y')
会报找不到 函数错误。比如 执行a.x()会报 错误:ImportError: cannot import name 'x'
小甲鱼原话:这个是循环嵌套导入问题。无论运行 a.py 或 b.py 哪一个文件都会抛出 ImportError 异常。这是因为在执行其中某一个文件(a.py)的加载过程中,会创建模块对象并执行对应的字节码。但当执行第一个语句的时候需要导入另一个文件(from b import y),因此 CPU 会转而去加载另一个文件(b.py)。同理,执行另一个文件的第一个语句(from a import x)恰好也是需要导入之前的文件(a.py)。此时,之前的文件处于仅导入第一条语句的阶段,因此其对应的字典中并不存在 x,故抛出“ImportError: cannot import name 'x'”异常。
解决方案是直接使用 import 语句导入
# a.py
import b
def x():
print('x')
# b.py
import a
def y():
print('y')
a.x()
我的问题: 感觉没有什么变化啊!就多了个a.x() 为什么用import 就不会报错了?(我试了一下改成import方式导入也会报错。。。)找到原因了,我把a.x()放 b.py 文件里面了,这样不行。我就把 a.x() 从b.py文件去掉 在另一个文件调用就行了。
已经解决小甲鱼P51讲动手第一题参考答案已经详细说明了执行过程
原话:因为在执行 b.py 的加载过程中,需要创建新的模块对象 b,然后执行 b.py 对应的字节码。当遇到第一条语句(import a)的时候,Python 会转而去导入 a.py 并生成模块对象 a。同样遇到第一条语句(import b)的时候,Python 就跑去导入模块 b,此时发现 b 模块已经导入(在 sys.modules 中存在),继而执行 b 模块的字节码,当执行到 a.x() 的时候,由于模块 a 此时并未完全导入,所以抛出AttributeError 异常。 )}`,ewF
~lPQM=JZk.0tf:$5N9uC*H7AG"Wb
怕有些鱼油可能看不懂,小甲鱼给大家整理下,看 Python 是如何被当成猴子耍的:?rY&)ITWA
执行 b.py -> import a -> 查找 a 模块 -> 未发现 a 模块对象 -> 导入 a.py -> import b -> 查找 b 模块 -> 发现 b 模块对象 -> 接着往下执行字节码(import a 已执行过,Python 有机制确保不重复导入,因而不会再执行) -> a.x() -> 在 a 模块中找不到 x(),因为 a 还没有被完全导入嘛……jY'$tgql
Sfw8mBohE;tG4J,ZO*1'DQi@
好了,解决的方案也很简单,用这节课的知识,就是使用 if __name__ == "__main__" 来确保 Python 不要在导入的过程中调用不该调用的函数。
所以应该这么写:来
# a.py
import b
def x():
print('x')
if __name__ == "__main__":
b.y()
# b.py
import a
def y():
print('y')
if __name__ == "__main__":
a.x()
你在b里面导入a的x,但是a却要导入b的y,你自己想想,会不会无限循环下去?
Python不允许这么无限循环。
如果你直接在b里面导入a而不是a的x,那跟x和y没有任何关系,所以不会报错。
xiaofeiyu 发表于 2020-6-21 17:28
你在b里面导入a的x,但是a却要导入b的y,你自己想想,会不会无限循环下去?
Python不允许这么无限循环。
...
也会报错 天下有敌 发表于 2020-6-21 20:39
也会报错
不能加a.x() xiaofeiyu 发表于 2020-6-21 20:47
不能加a.x()
那怎么确定不会报错? 天下有敌 发表于 2020-6-21 20:49
那怎么确定不会报错?
因为我就不会报错
这个主要原因是因为在导入过程中,b 需要访问 a 的变量 x , 但是 a 的变量 x 还没初始化导致的报错,具体循环导入过程应该可以这样看:
当你用from 导入模块时候会创建个字典,然后将要导入的内容放入字典中返回给需要导入的模块
首先,我们假设执行 a.py 的 from b import y ,发现需要导入的模块 b就会创建一个空字典,这个字典的作用域在 b 的全局
然后读取 b.py 模块,当执行到 from a import x 时,再次创建了个新空字典,作用范围在 b 文件的全局作用域
而后又读取了 a 模块,当再次执行到 from b import y 时,发现需要导入的模块 a 已经有个字典,所以直接访问这个字典里的 x 但是由于 a 的字典中还是空的,导致里面没有 x 这个方法 导致的报错
而解决办法就是避免再次创建字典时候不要去访问字典里面的 x 方法即可,使用 import 就可以解决这个问题
因为import 不会去提前访问字典中的方法,而是导入之后的 所以就不会报错
以上的是我个人理解,也不能太确定是否对错,但是大体上就是这么个意思,你也不用太纠结于此,循环导入我们知道怎避免就好~
页:
[1]