Python循环导入?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22187279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 00:27:37  来源:igfitidea点击:

Python circular importing?

pythonimportcircular-dependency

提问by CpILL

So i'm getting this error

所以我收到这个错误

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

and you can see that i use the same import statement further up and it works? Is there some unwritten rule about circular importing? How do i use the same class further down the call stack?

你可以看到我进一步使用了相同的导入语句并且它有效吗?关于循环导入有什么不成文的规定吗?我如何在调用堆栈的更深处使用相同的类?

采纳答案by Blckknght

I think the answer by jpmc26, while by no means wrong, comes down too heavily on circular imports. They can work just fine, if you set them up correctly.

我认为 jpmc26 的答案虽然绝对没有,但在循环导入方面过于重要。如果您正确设置它们,它们可以正常工作。

The easiest way to do so is to use import my_modulesyntax, rather than from my_module import some_object. The former will almost always work, even if my_moduleincluded imports us back. The latter only works if my_objectis already defined in my_module, which in a circular import may not be the case.

最简单的方法是使用import my_module语法,而不是from my_module import some_object. 前者几乎总是有效,即使my_module包含将我们导入回来。后者仅在my_object已在 中定义时才有效my_module,而在循环导入中可能并非如此。

To be specific to your case: Try changing entities/post.pyto do import physicsand then refer to physics.PostBodyrather than just PostBodydirectly. Similarly, change physics.pyto do import entities.postand then use entities.post.Postrather than just Post.

具体到您的情况:尝试更改entities/post.py为 doimport physics然后参考physics.PostBody而不是PostBody直接引用。同样,更改physics.py为 doimport entities.post然后使用entities.post.Post而不仅仅是Post

回答by jpmc26

When you import a module (or a member of it) for the first time, the code inside the module is executed sequentially like any other code; e.g., it is not treated any differently that the body of a function. An importis just a command like any other (assignment, a function call, def, class). Assuming your imports occur at the top of the script, then here's what's happening:

当你第一次导入一个模块(或者它的一个成员)时,模块内部的代码和其他代码一样按顺序执行;例如,对函数体的处理没有任何区别。Animport和其他命令一样只是一个命令(赋值、函数调用、defclass)。假设您的导入发生在脚本的顶部,那么会发生以下情况:

  • When you try to import Worldfrom world, the worldscript gets executed.
  • The worldscript imports Field, which causes the entities.fieldscript to get executed.
  • This process continues until you reach the entities.postscript because you tried to import Post
  • The entities.postscript causes physicsmodule to be executed because it tries to import PostBody
  • Finally, physicstries to import Postfrom entities.post
  • I'm not sure whether the entities.postmodule exists in memory yet, but it really doesn't matter. Either the module is not in memory, or the module doesn't yet have a Postmember because it hasn't finished executing to define Post
  • Either way, an error occurs because Postis not there to be imported
  • 当您尝试从 导入Worldworldworld脚本将被执行。
  • world脚本的进口Field,这将导致entities.field脚本得到执行。
  • 此过程将继续,直到您到达entities.post脚本,因为您尝试导入Post
  • entities.post脚本导致physics模块被执行,因为它试图导入PostBody
  • 最后,physics尝试Postentities.post
  • 我不确定该entities.post模块是否存在于内存中,但这确实无关紧要。模块不在内存中,或者模块还没有Post成员,因为它还没有完成定义Post
  • 无论哪种方式,都会发生错误,因为Post没有导入

So no, it's not "working further up in the call stack". This is a stack trace of where the error occurred, which means it errored out trying to import Postin that class. You shouldn't use circular imports. At best, it has negligible benefit (typically, nobenefit), and it causes problems like this. It burdens any developer maintaining it, forcing them to walk on egg shells to avoid breaking it. Refactor your module organization.

所以不,它不是“在调用堆栈中进一步工作”。这是错误发生位置的堆栈跟踪,这意味着它在尝试导入Post该类时出错。您不应该使用循环导入。充其量,它的好处可以忽略不计(通常,没有好处),并且会导致这样的问题。它给任何维护它的开发人员带来负担,迫使他们在蛋壳上行走以避免破坏它。重构你的模块组织。

回答by Malik A. Rumi

For those of you who, like me, come to this issue from Django, you should know that the docs provide a solution: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey

对于像我一样从 Django 遇到这个问题的人,您应该知道文档提供了解决方案:https: //docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey

"...To refer to models defined in another application, you can explicitly specify a model with the full application label. For example, if the Manufacturer model above is defined in another application called production, you'd need to use:

“...要引用在另一个应用程序中定义的模型,您可以明确指定具有完整应用程序标签的模型。例如,如果上面的制造商模型是在另一个名为 production 的应用程序中定义的,您需要使用:

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'production.Manufacturer',
        on_delete=models.CASCADE,
)

This sort of reference can be useful when resolving circular import dependencies between two applications...."

在解决两个应用程序之间的循环导入依赖关系时,这种引用非常有用。……”

回答by Gene Olson

To understand circular dependencies, you need to remember that Python is essentially a scripting language. Execution of statements outside methods occurs at compile time. Import statements are executed just like method calls, and to understand them you should think about them like method calls.

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

When you do an import, what happens depends on whether the file you are importing already exists in the module table. If it does, Python uses whatever is currently in the symbol table. If not, Python begins reading the module file, compiling/executing/importing whatever it finds there. Symbols referenced at compile time are found or not, depending on whether they have been seen, or are yet to be seen by the compiler.

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

Imagine you have two source files:

假设您有两个源文件:

File X.py

文件 X.py

def X1:
    return "x1"

from Y import Y2

def X2:
    return "x2"

File Y.py

文件 Y.py

def Y1:
    return "y1"

from X import X1

def Y2:
    return "y2"

Now suppose you compile file X.py. The compiler begins by defining the method X1, and then hits the import statement in X.py. This causes the compiler to pause compilation of X.py and begin compiling Y.py. Shortly thereafter the compiler hits the import statement in Y.py. Since X.py is already in the module table, Python uses the existing incomplete X.py symbol table to satisfy any references requested. Any symbols appearing before the import statement in X.py are now in the symbol table, but any symbols after are not. Since X1 now appears before the import statement, it is successfully imported. Python then resumes compiling Y.py. In doing so it defines Y2 and finishes compiling Y.py. It then resumes compilation of X.py, and finds Y2 in the Y.py symbol table. Compilation eventually completes w/o error.

现在假设您编译文件 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。编译最终完成,没有错误。

Something very different happens if you attempt to compile Y.py from the command line. While compiling Y.py, the compiler hits the import statement before it defines Y2. Then it starts compiling X.py. Soon it hits the import statement in X.py that requires Y2. But Y2 is undefined, so the compile fails.

如果您尝试从命令行编译 Y.py,则会发生非常不同的事情。在编译 Y.py 时,编译器会在定义 Y2 之前命中 import 语句。然后它开始编译 X.py。很快它就遇到了 X.py 中需要 Y2 的 import 语句。但是 Y2 未定义,因此编译失败。

Please note that if you modify X.py to import Y1, the compile will always succeed, no matter which file you compile. However if you modify file Y.py to import symbol X2, neither file will compile.

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

Any time when module X, or any module imported by X might import the current module, do NOT use:

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

from X import Y

Any time you think there may be a circular import you should also avoid compile time references to variables in other modules. Consider the innocent looking code:

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

import X
z = X.Y

Suppose module X imports this module before this module imports X. Further suppose Y is defined in X after the import statement. Then Y will not be defined when this module is imported, and you will get a compile error. If this module imports Y first, you can get away with it. But when one of your co-workers innocently changes the order of definitions in a third module, the code will break.

假设模块 X 在此模块导入 X 之前导入此模块。进一步假设 Y 是在 import 语句之后的 X 中定义的。那么这个模块导入的时候Y就不会被定义了,就会出现编译错误。如果这个模块先导入 Y,你就可以摆脱它。但是,当您的一位同事无意中更改了第三个模块中的定义顺序时,代码就会中断。

In some cases you can resolve circular dependencies by moving an import statement down below symbol definitions needed by other modules. In the examples above, definitions before the import statement never fail. Definitions after the import statement sometimes fail, depending on the order of compilation. You can even put import statements at the end of a file, so long as none of the imported symbols are needed at compile time.

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

Note that moving import statements down in a module obscures what you are doing. Compensate for this with a comment at the top of your module something like the following:

请注意,在模块中向下移动 import 语句会掩盖您正在执行的操作。使用模块顶部的注释对此进行补偿,如下所示:

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

In general this is a bad practice, but sometimes it is difficult to avoid.

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

回答by Andreas Bergstr?m

If you run into this issue in a fairly complex app it can be cumbersome to refactor all your imports. PyCharm offers a quickfix for this that will automatically change all usage of the imported symbols as well.

如果您在相当复杂的应用程序中遇到此问题,则重构所有导入可能会很麻烦。PyCharm 为此提供了一个快速修复程序,它也会自动更改导入符号的所有用法。

enter image description here

在此处输入图片说明

回答by MKJ

I was using the following:

我正在使用以下内容:

from module import Foo

foo_instance = Foo()

but to get rid of circular referenceI did the following and it worked:

但是为了摆脱circular reference我做了以下事情并且它起作用了:

import module.foo

foo_instance = foo.Foo()

回答by Alexander Shubert

I was able to import the module within the function (only) that would require the objects from this module:

我能够在需要来自该模块的对象的函数(仅)中导入模块:

def my_func():
    import Foo
    foo_instance = Foo()