为了账号安全,请及时绑定邮箱和手机立即绑定

Python循环导入?

Python循环导入?

手掌心 2019-07-05 14:54:51
Python循环导入?所以我得到了这个错误Traceback (most recent call last):   File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module>     from world import World   File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module>     from entities.field import Field   File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module>     from entities.goal import Goal   File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module>     from entities.post import Post   File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module>     from physics import PostBody   File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module>     from entities.post import PostImportError: cannot import name Post您可以看到,我在上面使用了相同的import语句,它可以工作吗?关于循环进口有不成文的规定吗?如何在调用堆栈的后面使用相同的类?
查看完整描述

3 回答

?
尚方宝剑之说

TA贡献1788条经验 获得超4个赞

要理解循环依赖关系,您需要记住Python本质上是一种脚本语言。方法外部语句的执行在编译时发生。导入语句就像方法调用一样执行,要理解它们,您应该像方法调用一样考虑它们。

当您进行导入时,所发生的事情取决于您要导入的文件是否已经存在于模块表中。如果使用,Python将使用符号表中当前的任何内容。否则,Python将开始读取模块文件,编译/执行/导入它在那里找到的任何内容。编译时引用的符号是否被找到,取决于它们是否已经被看到,或者编译器还没有看到它们。

假设您有两个源文件:

文件X.py

def X1:
    return "x1"from Y import Y2def X2:
    return "x2"

文件Y.py

def Y1:
    return "y1"from X import X1def Y2:
    return "y2"

现在假设您编译了X.py文件。编译器首先定义X1方法,然后点击X.py中的import语句。这导致编译器暂停X.py的编译并开始编译Y.py。此后不久,编译器将命中Y.py中的import语句。由于X.py已经在模块表中,Python使用现有的不完整X.py符号表来满足任何请求的引用。在X.py中的import语句之前出现的任何符号现在都在符号表中,但是后面的任何符号都不是。因为X1现在出现在IMPORT语句之前,所以它已成功导入。然后Python继续编译Y.py。在这样做时,它定义了Y2并完成了对Y.py的编译。然后继续编译X.py,并在Y.py符号表中找到Y2。编译最终完成w/o错误。

如果尝试从命令行编译Y.py,则会发生非常不同的情况。在编译Y.py时,编译器在定义Y2之前点击import语句。然后开始编译X.py。很快,它就会命中X.py中的import语句,该语句需要Y2。但是Y2没有定义,所以编译失败。

请注意,如果您修改X.py以导入Y1,编译将始终成功,无论您编译的是哪个文件。但是,如果您修改文件Y.py以导入符号X2,则两个文件都不会编译。

当模块X或X导入的任何模块可能导入当前模块时,请不要使用:

from X import Y

当您认为可能存在循环导入时,您也应该避免编译时引用其他模块中的变量。考虑一下看似无辜的代码:

import X
z = X.Y

假设模块X在这个模块导入X之前导入这个模块。进一步假设Y是在导入语句之后在X中定义的。在导入该模块时,将不会定义Y,您将得到一个编译错误。如果这个模块首先导入Y,您就可以逃脱它。但是,当您的一个同事无意地更改了第三个模块中定义的顺序时,代码就会中断。

在某些情况下,您可以通过将导入语句向下移到其他模块所需的符号定义下面来解决循环依赖关系。在上面的例子中,导入语句之前的定义永远不会失败。导入语句后的定义有时会失败,这取决于编译的顺序。您甚至可以将导入语句放在文件的末尾,只要编译时不需要导入的符号。

请注意,在模块中向下移动导入语句会掩盖您正在做的事情。在模块顶部加上一个注释来弥补这一点,如下所示:

#import X   (actual import moved down to avoid circular dependency)

一般来说,这是一个不好的做法,但有时是很难避免的。


查看完整回答
反对 回复 2019-07-05
  • 3 回答
  • 0 关注
  • 620 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信