问题: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 Post
ImportError: cannot import name Post
并且您可以看到我进一步使用了相同的import语句,并且有效吗?关于循环导入是否有一些不成文的规定?如何在调用堆栈的更下方使用相同的类?
回答 0
我认为jpmc26的答案,但绝不是错误的,但在循环进口方面却过于严格。如果正确设置它们,它们可以正常工作。
最简单的方法是使用import my_module
语法,而不是from my_module import some_object
。前者几乎总是可以工作,即使my_module
包括在内也能使我们重新受益。后者只有在my_object
中已经定义时才有效my_module
,在循环导入中可能不是这种情况。
要针对您的具体情况:尝试更改entities/post.py
为do import physics
,然后引用physics.PostBody
而不是PostBody
直接引用。同样,更改physics.py
为do import entities.post
,然后使用entities.post.Post
而不是just Post
。
回答 1
首次导入模块(或其成员)时,模块内的代码将像其他任何代码一样顺序执行。例如,对函数主体的处理没有任何区别。An import
只是一个与其他命令一样的命令(赋值,函数调用def
,class
)。假设您的导入发生在脚本的顶部,那么将发生以下情况:
- 当您尝试从导入
World
时world
,world
脚本将被执行。 - 该
world
脚本的进口Field
,这将导致entities.field
脚本得到执行。 - 这个过程一直持续到您到达
entities.post
脚本为止,因为您尝试导入Post
- 该
entities.post
脚本导致physics
模块被执行,因为它尝试导入PostBody
- 最后,
physics
尝试Post
从entities.post
- 我不确定该
entities.post
模块是否已存在于内存中,但这并不重要。该模块不在内存中,或者该模块还没有Post
成员,因为该模块尚未完成执行定义Post
- 无论哪种方式,都会发生错误,因为
Post
那里没有要导入的错误
因此,它不是“在调用堆栈中进一步发挥作用”。这是错误发生位置的堆栈跟踪,这意味着它在尝试导入Post
该类时出错。您不应该使用循环导入。充其量,它的收益微不足道(通常没有收益),并且会引起类似的问题。这给所有开发人员维护它带来了负担,迫使他们在蛋壳上行走以避免损坏它。重构您的模块组织。
回答 2
要了解循环依赖关系,您需要记住Python本质上是一种脚本语言。方法外部的语句执行在编译时发生。导入语句的执行就像方法调用一样,要理解它们,您应该像方法调用一样考虑它们。
导入时,发生的情况取决于模块表中是否已存在要导入的文件。如果是这样,Python将使用符号表中当前使用的任何内容。如果没有,Python将开始读取模块文件,编译/执行/导入其找到的文件。是否找到在编译时引用的符号,具体取决于编译器是否已看到它们。
假设您有两个源文件:
文件X.py
def X1:
return "x1"
from Y import Y2
def X2:
return "x2"
文件Y.py
def Y1:
return "y1"
from X import X1
def 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。编译最终完成,没有错误。
如果尝试从命令行编译Y.py,则会发生非常不同的事情。在编译Y.py时,编译器会在定义Y2之前命中import语句。然后,它开始编译X.py。很快,它在X.py中命中了需要Y2的import语句。但是Y2是未定义的,因此编译失败。
请注意,如果您将X.py修改为导入Y1,则无论您编译哪个文件,编译都将始终成功。但是,如果修改文件Y.py以导入符号X2,则两个文件都不会编译。
每当模块X或X导入的任何模块可能导入当前模块时,请勿使用:
from X import Y
每当您认为可能会有循环导入时,也应避免在编译时引用其他模块中的变量。考虑一下看起来纯真的代码:
import X
z = X.Y
假设模块X在此模块导入X之前先导入此模块。进一步假设Y在import语句后的X中定义。然后,在导入此模块时将不会定义Y,并且会出现编译错误。如果此模块首先导入Y,那么您可以摆脱它。但是,当您的一位同事无辜地更改第三个模块中的定义顺序时,代码将中断。
在某些情况下,您可以通过将导入语句下移到其他模块所需的符号定义下方来解决循环依赖性。在上面的示例中,import语句之前的定义永远不会失败。取决于编译的顺序,import语句之后的定义有时会失败。您甚至可以将import语句放在文件的末尾,只要在编译时不需要任何导入的符号即可。
请注意,将导入语句在模块中下移会掩盖您的操作。为此,请在模块顶部添加注释,如下所示:
#import X (actual import moved down to avoid circular dependency)
通常,这是一个不好的做法,但有时很难避免。
回答 3
对于像我一样从Django来解决此问题的人,您应该知道该文档提供了一种解决方案:https : //docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey
“ …要引用在另一个应用程序中定义的模型,您可以显式指定带有完整应用程序标签的模型。例如,如果上述制造商模型是在另一个名为production的应用程序中定义的,则需要使用:
class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer',
on_delete=models.CASCADE,
)
解决两个应用程序之间的循环导入依赖关系时,此类参考很有用。…”
回答 4
我能够(仅)将需要该模块中对象的功能导入模块:
def my_func():
import Foo
foo_instance = Foo()
回答 5
回答 6
我正在使用以下内容:
from module import Foo
foo_instance = Foo()
但摆脱掉circular reference
我做了以下工作,它的工作:
import module.foo
foo_instance = foo.Foo()