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语句,并且可以正常工作吗?关于循环导入是否有一些不成文的规定?如何在调用堆栈的更下方使用同一类?
#1 楼
我认为jpmc26的答案虽然绝对没有错,但在循环进口方面却过于严格。如果正确设置它们,它们可以正常工作。最简单的方法是使用
import my_module
语法,而不是from my_module import some_object
。即使my_module
包含了我们的进口,前者几乎总是可以使用。后者仅在my_object
中已定义my_module
时才有效,在循环导入中可能不是这种情况。要针对您的情况:尝试更改
entities/post.py
以执行import physics
,然后参考physics.PostBody
比直接PostBody
更多。同样,将physics.py
更改为import entities.post
,然后使用entities.post.Post
而不是Post
。#2 楼
首次导入模块(或其成员)时,模块内的代码将像其他任何代码一样依次执行。例如,对函数主体的处理没有任何不同。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
时出错。您不应该使用循环导入。充其量,它的收益微不足道(通常没有收益),并且会引起类似的问题。这给所有开发人员维护它带来了负担,迫使他们在蛋壳上行走以避免损坏它。重构您的模块组织。评论
应该是isinstance(userData,Post)。无论如何,您别无选择。循环导入无效。您具有循环进口的事实对我来说是一种代码气味。它建议您具有一些应移至第三个模块的功能。我不能不讲全部两个课程就说什么。
– jpmc26
2014年5月5日在2:53
@CpILL过了一会儿,我确实遇到了一个非常棘手的选择。如果您暂时无法执行此操作(由于时间限制或您有什么原因),则可以在使用该方法的本地本地进行导入。直到调用函数后,def内部的函数主体才会执行,因此,直到您实际调用函数后,导入才会发生。到那时,导入应该可以进行了,因为其中一个模块在调用之前就已经完全导入了。那是绝对令人作呕的黑客,并且不应在任何长时间内保留在您的代码库中。
– jpmc26
2014年5月5日20:30
我认为您对循环进口的回答太难了。如果仅从foo而不是从foo import Bar导入foo,则循环导入通常有效。这是因为大多数模块只是定义稍后运行的内容(例如函数和类)。导入重要模块的模块(例如,如果__name__ ==“ __main__”不受脚本保护的脚本)可能仍然很麻烦,但这并不是太常见。
– Blckknght
2014年5月5日22:33
@Blckknght我认为您正在准备将时间花在解决其他怪异问题上,如果您使用循环进口,其他人将不得不调查这些问题并感到困惑。它们迫使您花时间小心,不要绊倒它们,最重要的是,您的设计需要重构代码臭味。对于它们在技术上是否可行,我可能是错的,但它们注定是一个糟糕的设计选择,注定迟早会引起问题。清晰和简单是编程中的圣旨,循环导入在我的书中都违反了。
– jpmc26
2014年3月12日23:39
或者您对功能的划分过多,这就是循环导入的原因。如果您有两件事始终相互依赖;最好将它们放在一个文件中。 Python不是Java;它不是Java。没有理由不将功能/类分组到一个文件中,以防止奇怪的导入逻辑。 :-)
–马克·里鲍(Mark Ribau)
15年3月16日在18:34
#3 楼
要了解循环依赖关系,您需要记住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
任何时候,只要您认为可能会有循环导入,就应该避免编译时引用其他模块中的变量。考虑一下看起来很纯洁的代码:假设模块X在此模块导入X之前先导入了此模块。另外,假设Y在import语句之后在X中定义了Y。然后,在导入此模块时将不会定义Y,并且会出现编译错误。如果此模块首先导入Y,那么您可以摆脱它。但是,当您的一位同事无辜地更改第三个模块中的定义顺序时,代码将中断。
在某些情况下,您可以通过将导入语句下移到所需的符号定义下方来解决循环依赖性。通过其他模块。在上面的示例中,import语句之前的定义永远不会失败。取决于编译的顺序,import语句之后的定义有时会失败。只要在编译时不需要导入的符号,甚至可以将import语句放在文件的末尾。
请注意,将import语句向下移入模块会使您的工作变得晦涩。为此,请在模块顶部添加注释,如下所示:
import X
z = X.Y
一般来说,这是一个不好的做法,但有时很难避免。
评论
我认为python中根本没有编译器或编译时间
–王erie
17年7月8日在13:17
Python确实有一个编译器,并且已编译@pkqxdd,编译通常只对用户隐藏。这可能有点令人困惑,但是对于作者而言,如果不参考一些Python的“编译时”,就很难给出这种令人赞叹的清晰描述,说明正在发生的事情是很困难的。
–汉克
17年10月31日在19:48
@pkqxddnedbatchelder.com/blog/201803/…
– jpmc26
19年8月21日在20:42
我继续在我的机器上尝试此操作,并得到了不同的结果。跑了X.py,但出现错误“无法从'Y'导入名称'Y2'”。 Ran Y.py没问题。我使用的是Python 3.7.5,您能帮忙解释一下这里的问题吗?
–黄学峰
1月8日10:52
#4 楼
对于像我一样从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,
)
这种引用在解决两个应用程序之间的循环导入依赖关系时非常有用....“
评论
我知道我不应该在评论中说“谢谢”,但这已经困扰了我几个小时。谢谢你,谢谢你,谢谢你!!!
– MikeyE
18-2-25在5:04
我同意@MikeyE。我读过几个博客和Stackoverflows试图用PonyORM来解决这个问题。在其他人说这是不好的做法的地方,或者为什么要将类编码为循环的,ORM正是发生这种情况的地方。因为很多示例都将所有模型都放在同一个文件中,并且我们遵循这些示例(除了我们每个文件都使用一个模型),所以当Python无法编译时,问题还不清楚。然而,答案是如此简单。正如Mike所指出的,非常感谢。
–trash80
19年11月16日17:13
#5 楼
我能够(仅)将需要该模块中对象的功能导入模块:def my_func():
import Foo
foo_instance = Foo()
评论
python多么优雅
– Yaro
8月17日21:13
#6 楼
如果您在一个相当复杂的应用程序中遇到此问题,则重构所有导入内容可能会很麻烦。 PyCharm为此提供了一个快速修复程序,该修复程序还将自动更改导入符号的所有用法。#7 楼
我正在使用以下代码:from module import Foo
foo_instance = Foo()
但是要摆脱
circular reference
,我做了以下工作,并且有效:import module.foo
foo_instance = foo.Foo()
评论
这个答案与相对进口兼容吗?
–乔
2014年12月30日在21:45
为什么会这样?
– Juan Pablo Santos
15年7月21日在19:19
说非源语法将始终有效是错误的。如果我有A类(对象):通过; C(b.B)类:传递模块a和B(a.A)类:传递模块b,那么循环导入仍然是一个问题,这将无法正常工作。
–CrazyCasta
15年7月30日在3:46
没错,模块顶级代码中的任何循环依赖项(例如示例中的类声明的基类)都将成为问题。在这种情况下,您应该重构jpmc的答案以重构模块组织可能是100%正确的。可以将B类移到模块a中,或者将C类移到模块b中,这样就可以打破循环。还要注意的是,即使圆的一个方向只涉及顶层代码(例如,如果C类不存在),也可能会出错,具体取决于哪个模块是由其他代码首先导入的。
– Blckknght
15年7月30日在7:08
@TylerCrompton:我不确定“模块导入必须是绝对的”是什么意思。只要您要导入模块,循环相对导入就可以正常工作,而不是其内容(例如,从.import sibling_module导入,而不是从.sibling_module import SomeClass导入)。循环导入涉及包的__init__.py文件时,还有一些更细微的问题,但是这个问题很少见,而且可能是导入实现中的错误。请参阅Python错误23447,我提交了一个补丁(可惜的是)。
– Blckknght
16年5月6日在1:48