标签归档:circular-dependency

Python循环导入?

问题: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语句,并且有效吗?关于循环导入是否有一些不成文的规定?如何在调用堆栈的更下方使用相同的类?

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?


回答 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

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.

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

To be specific to your case: Try changing entities/post.py to do import physics and then refer to physics.PostBody rather than just PostBody directly. Similarly, change physics.py to do import entities.post and then use entities.post.Post rather than just Post.


回答 1

首次导入模块(或其成员)时,模块内的代码将像其他任何代码一样顺序执行。例如,对函数主体的处理没有任何区别。An import只是一个与其他命令一样的命令(赋值,函数调用defclass)。假设您的导入发生在脚本的顶部,那么将发生以下情况:

  • 当您尝试从导入Worldworldworld脚本将被执行。
  • world脚本的进口Field,这将导致entities.field脚本得到执行。
  • 这个过程一直持续到您到达entities.post脚本为止,因为您尝试导入Post
  • entities.post脚本导致physics模块被执行,因为它尝试导入PostBody
  • 最后,physics尝试Postentities.post
  • 我不确定该entities.post模块是否已存在于内存中,但这并不重要。该模块不在内存中,或者该模块还没有Post成员,因为该模块尚未完成执行定义Post
  • 无论哪种方式,都会发生错误,因为Post那里没有要导入的错误

因此,它不是“在调用堆栈中进一步发挥作用”。这是错误发生位置的堆栈跟踪,这意味着它在尝试导入Post该类时出错。您不应该使用循环导入。充其量,它的收益微不足道(通常没有收益),并且会引起类似的问题。这给所有开发人员维护它带来了负担,迫使他们在蛋壳上行走以避免损坏它。重构您的模块组织。

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 import is 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:

  • When you try to import World from world, the world script gets executed.
  • The world script imports Field, which causes the entities.field script to get executed.
  • This process continues until you reach the entities.post script because you tried to import Post
  • The entities.post script causes physics module to be executed because it tries to import PostBody
  • Finally, physics tries to import Post from entities.post
  • I’m not sure whether the entities.post module 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 Post member because it hasn’t finished executing to define Post
  • Either way, an error occurs because Post is not there to be imported

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 Post in that class. You shouldn’t use circular imports. At best, it has negligible benefit (typically, no benefit), 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.


回答 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)

通常,这是一个不好的做法,但有时很难避免。

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.

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.

Imagine you have two source files:

File X.py

def X1:
    return "x1"

from Y import Y2

def X2:
    return "x2"

File 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.

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.

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.

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

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.

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.

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 X   (actual import moved down to avoid circular dependency)

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


回答 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,
)

解决两个应用程序之间的循环导入依赖关系时,此类参考很有用。…”

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

“…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:

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.…”


回答 4

我能够(仅)将需要该模块中对象的功能导入模块:

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

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()

回答 5

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

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.


回答 6

我正在使用以下内容:

from module import Foo

foo_instance = Foo()

但摆脱掉circular reference我做了以下工作,它的工作:

import module.foo

foo_instance = foo.Foo()

I was using the following:

from module import Foo

foo_instance = Foo()

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

import module.foo

foo_instance = foo.Foo()

Python中的循环(或循环)导入

问题:Python中的循环(或循环)导入

如果两个模块相互导入会怎样?

为了概括这个问题,Python中的循环导入怎么办?

What will happen if two modules import each other?

To generalize the problem, what about the cyclic imports in Python?


回答 0

去年在comp.lang.python上对此进行了非常好的讨论。它相当彻底地回答了您的问题。

导入确实非常简单。只要记住以下几点:

‘import’和’from xxx import yyy’是可执行语句。它们在运行的程序到达该行时执行。

如果模块不在sys.modules中,则导入将在sys.modules中创建新的模块条目,然后在模块中执行代码。在执行完成之前,它不会将控制权返回给调用模块。

如果sys.modules中确实存在某个模块,则无论导入是否完成执行,导入都会简单地返回该模块。这就是循环导入可能返回部分为空的模块的原因。

最后,执行脚本在名为__main__的模块中运行,以其自己的名称导入脚本将创建一个与__main__无关的新模块。

放在一起,导入模块时就不会感到惊讶。

There was a really good discussion on this over at comp.lang.python last year. It answers your question pretty thoroughly.

Imports are pretty straightforward really. Just remember the following:

‘import’ and ‘from xxx import yyy’ are executable statements. They execute when the running program reaches that line.

If a module is not in sys.modules, then an import creates the new module entry in sys.modules and then executes the code in the module. It does not return control to the calling module until the execution has completed.

If a module does exist in sys.modules then an import simply returns that module whether or not it has completed executing. That is the reason why cyclic imports may return modules which appear to be partly empty.

Finally, the executing script runs in a module named __main__, importing the script under its own name will create a new module unrelated to __main__.

Take that lot together and you shouldn’t get any surprises when importing modules.


回答 1

如果在import foo内部barimport bar内部进行操作foo,它将正常工作。到实际运行任何东西时,两个模块都将完全加载,并且将相互引用。

问题是当您改为from foo import abc和时from bar import xyz。因为现在每个模块都需要先导入另一个模块(这样才能导入我们要导入的名称),然后才能导入它。

If you do import foo inside bar and import bar inside foo, it will work fine. By the time anything actually runs, both modules will be fully loaded and will have references to each other.

The problem is when instead you do from foo import abc and from bar import xyz. Because now each module requires the other module to already be imported (so that the name we are importing exists) before it can be imported.


回答 2

循环导入会终止,但是您需要注意不要在模块初始化期间使用循环导入的模块。

考虑以下文件:

a.py:

print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"

b.py:

print "b in"
import a
print "b out"
x = 3

如果执行a.py,您将获得以下信息:

$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out

在第二次导入b.py时(在第二个中a in),Python解释器不会b再次导入,因为它已经存在于模块dict中。

如果您尝试在模块初始化期间b.x从中进行访问a,则会显示AttributeError

将以下行添加到a.py

print b.x

然后,输出为:

$ python a.py
a in                    
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
  File "a.py", line 4, in <module>
    import b
  File "/home/shlomme/tmp/x/b.py", line 2, in <module>
    import a
 File "/home/shlomme/tmp/x/a.py", line 7, in <module>
    print b.x
AttributeError: 'module' object has no attribute 'x'

这是因为模块是在导入时执行的,并且在b.x访问时间时,该行x = 3尚未执行,只有在之后才执行b out

Cyclic imports terminate, but you need to be careful not to use the cyclically-imported modules during module initialization.

Consider the following files:

a.py:

print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"

b.py:

print "b in"
import a
print "b out"
x = 3

If you execute a.py, you’ll get the following:

$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out

On the second import of b.py (in the second a in), the Python interpreter does not import b again, because it already exists in the module dict.

If you try to access b.x from a during module initialization, you will get an AttributeError.

Append the following line to a.py:

print b.x

Then, the output is:

$ python a.py
a in                    
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
  File "a.py", line 4, in <module>
    import b
  File "/home/shlomme/tmp/x/b.py", line 2, in <module>
    import a
 File "/home/shlomme/tmp/x/a.py", line 7, in <module>
    print b.x
AttributeError: 'module' object has no attribute 'x'

This is because modules are executed on import and at the time b.x is accessed, the line x = 3 has not be executed yet, which will only happen after b out.


回答 3

正如其他答案所描述的那样,这种模式在python中是可以接受的:

def dostuff(self):
     from foo import bar
     ...

当其他模块导入文件时,这将避免执行import语句。仅当存在逻辑循环依赖关系时,这才会失败。

大多数循环导入实际上不是逻辑循环导入,而是引发ImportError错误,因为import()调用时会评估整个文件的顶级语句。

ImportErrors如果您确实希望进口货物居于首位,则几乎可以避免这些情况

考虑以下循环导入:

应用程式A

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

应用程式B

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

大卫·比兹利(David Beazleys)精彩演讲模块和软件包:活着,让自己死!-PyCon 20151:54:00这是在python中处理循环导入的一种方法:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

这会尝试导入SimplifiedImageSerializer,如果ImportError引发,因为已经被导入,它将从importcache中将其拉出。

PS:您必须以David Beazley的声音阅读整篇文章。

As other answers describe this pattern is acceptable in python:

def dostuff(self):
     from foo import bar
     ...

Which will avoid the execution of the import statement when the file is imported by other modules. Only if there is a logical circular dependency, this will fail.

Most Circular Imports are not actually logical circular imports but rather raise ImportError errors, because of the way import() evaluates top level statements of the entire file when called.

These ImportErrors can almost always be avoided if you positively want your imports on top:

Consider this circular import:

App A

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

App B

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

From David Beazleys excellent talk Modules and Packages: Live and Let Die! – PyCon 2015, 1:54:00, here is a way to deal with circular imports in python:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

This tries to import SimplifiedImageSerializer and if ImportError is raised, because it already is imported, it will pull it from the importcache.

PS: You have to read this entire post in David Beazley’s voice.


回答 4

我这里有一个让我印象深刻的例子!

foo.py

import bar

class gX(object):
    g = 10

bar.py

from foo import gX

o = gX()

main.py

import foo
import bar

print "all done"

在命令行中: $ python main.py

Traceback (most recent call last):
  File "m.py", line 1, in <module>
    import foo
  File "/home/xolve/foo.py", line 1, in <module>
    import bar
  File "/home/xolve/bar.py", line 1, in <module>
    from foo import gX
ImportError: cannot import name gX

I got an example here that struck me!

foo.py

import bar

class gX(object):
    g = 10

bar.py

from foo import gX

o = gX()

main.py

import foo
import bar

print "all done"

At the command line: $ python main.py

Traceback (most recent call last):
  File "m.py", line 1, in <module>
    import foo
  File "/home/xolve/foo.py", line 1, in <module>
    import bar
  File "/home/xolve/bar.py", line 1, in <module>
    from foo import gX
ImportError: cannot import name gX

回答 5

模块a.py:

import b
print("This is from module a")

模块b.py

import a
print("This is from module b")

运行“模块a”将输出:

>>> 
'This is from module a'
'This is from module b'
'This is from module a'
>>> 

它输出这3行,而由于循环导入而应该输出无穷大。下面列出了在运行“模块a”时逐行发生的情况:

  1. 第一行是 import b。因此它将访问模块b
  2. 模块b的第一行是 import a。因此它将访问模块a
  3. 模块a的第一行是,import b但是请注意,此行将不再执行,因为python中的每个文件仅执行一次导入行,因此无论在何时何地执行都无关紧要。因此它将传递到下一行并打印"This is from module a"
  4. 从模块b访问完整个模块a后,我们仍在模块b中。所以下一行会打印"This is from module b"
  5. 模块b行完全执行。因此,我们将回到模块b的起始模块a。
  6. import b行已经执行,将不再执行。下一行将打印"This is from module a",程序将完成。

Module a.py :

import b
print("This is from module a")

Module b.py

import a
print("This is from module b")

Running “Module a” will output:

>>> 
'This is from module a'
'This is from module b'
'This is from module a'
>>> 

It output this 3 lines while it was supposed to output infinitival because of circular importing. What happens line by line while running”Module a” is listed here:

  1. The first line is import b. so it will visit module b
  2. The first line at module b is import a. so it will visit module a
  3. The first line at module a is import b but note that this line won’t be executed again anymore, because every file in python execute an import line just for once, it does not matter where or when it is executed. so it will pass to the next line and print "This is from module a".
  4. After finish visiting whole module a from module b, we are still at module b. so the next line will print "This is from module b"
  5. Module b lines are executed completely. so we will go back to module a where we started module b.
  6. import b line have been executed already and won’t be executed again. the next line will print "This is from module a" and program will be finished.

回答 6

我完全同意pythoneer的回答。但是我偶然发现了一些有循环导入缺陷的代码,并在尝试添加单元测试时引起了问题。因此,在不做任何更改的情况下快速修补它,可以通过动态导入解决问题。

# Hack to import something without circular import issue
def load_module(name):
    """Load module using imp.find_module"""
    names = name.split(".")
    path = None
    for name in names:
        f, path, info = imp.find_module(name, path)
        path = [path]
    return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")

同样,这不是永久性的修复,但是可以帮助想要修复导入错误而无需更改太多代码的人。

干杯!

I completely agree with pythoneer’s answer here. But I have stumbled on some code that was flawed with circular imports and caused issues when trying to add unit tests. So to quickly patch it without changing everything you can resolve the issue by doing a dynamic import.

# Hack to import something without circular import issue
def load_module(name):
    """Load module using imp.find_module"""
    names = name.split(".")
    path = None
    for name in names:
        f, path, info = imp.find_module(name, path)
        path = [path]
    return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")

Again, this isn’t a permanent fix but may help someone that wants to fix an import error without changing too much of the code.

Cheers!


回答 7

这里有很多很好的答案。尽管通常可以快速解决问题,但有些解决方案比其他解决方案更具有Python风格,但如果您愿意进行一些重构,则另一种方法是分析代码的组织,并尝试消除循环依赖。例如,您可能会发现您具有:

档案a.py

from b import B

class A:
    @staticmethod
    def save_result(result):
        print('save the result')

    @staticmethod
    def do_something_a_ish(param):
        A.save_result(A.use_param_like_a_would(param))

    @staticmethod
    def do_something_related_to_b(param):
        B.do_something_b_ish(param)

文件b.py

from a import A

class B:
    @staticmethod
    def do_something_b_ish(param):
        A.save_result(B.use_param_like_b_would(param))

在这种情况下,只需将一个静态方法移至单独的文件,例如c.py

文件c.py

def save_result(result):
    print('save the result')

将允许save_result从A中删除方法,从而允许从b中的a中删除A的导入:

重构文件a.py

from b import B
from c import save_result

class A:
    @staticmethod
    def do_something_a_ish(param):
        A.save_result(A.use_param_like_a_would(param))

    @staticmethod
    def do_something_related_to_b(param):
        B.do_something_b_ish(param)

重构文件b.py

from c import save_result

class B:
    @staticmethod
    def do_something_b_ish(param):
        save_result(B.use_param_like_b_would(param))

总而言之,如果您有一个报告静态方法的工具(例如pylint或PyCharm),则仅在其上添加staticmethod修饰符可能不是使警告消失的最佳方法。即使该方法似乎与该类有关,也最好将其分开,尤其是当您有几个紧密相关的模块可能需要相同的功能并且打算实践DRY原理时。

There are a lot of great answers here. While there are usually quick solutions to the problem, some of which feel more pythonic than others, if you have the luxury of doing some refactoring, another approach is to analyze the organization of your code, and try to remove the circular dependency. You may find, for example, that you have:

File a.py

from b import B

class A:
    @staticmethod
    def save_result(result):
        print('save the result')

    @staticmethod
    def do_something_a_ish(param):
        A.save_result(A.use_param_like_a_would(param))

    @staticmethod
    def do_something_related_to_b(param):
        B.do_something_b_ish(param)

File b.py

from a import A

class B:
    @staticmethod
    def do_something_b_ish(param):
        A.save_result(B.use_param_like_b_would(param))

In this case, just moving one static method to a separate file, say c.py:

File c.py

def save_result(result):
    print('save the result')

will allow removing the save_result method from A, and thus allow removing the import of A from a in b:

Refactored File a.py

from b import B
from c import save_result

class A:
    @staticmethod
    def do_something_a_ish(param):
        A.save_result(A.use_param_like_a_would(param))

    @staticmethod
    def do_something_related_to_b(param):
        B.do_something_b_ish(param)

Refactored File b.py

from c import save_result

class B:
    @staticmethod
    def do_something_b_ish(param):
        save_result(B.use_param_like_b_would(param))

In summary, if you have a tool (e.g. pylint or PyCharm) that reports on methods that can be static, just throwing a staticmethod decorator on them might not be the best way to silence the warning. Even though the method seems related to the class, it might be better to separate it out, especially if you have several closely related modules that might need the same functionality and you intend to practice DRY principles.


回答 8

循环导入可能会造成混淆,因为导入有两件事:

  1. 它执行导入的模块代码
  2. 将导入模块添加到导入模块全局符号表中

前者仅执行一次,而后者在每个import语句中执行。当导入模块使用部分执行代码的已导入模块时,循环导入会产生情况。因此,它将不会看到import语句之后创建的对象。下面的代码示例对此进行了演示。

循环进口并不是不惜一切代价避免的最终罪恶。在诸如Flask之类的某些框架中,它们是很自然的,调整您的代码以消除它们并不能使代码变得更好。

main.py

print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
    print 'imports done'
    print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)

b.by

print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"

py

print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"

带有注释的python main.py输出

import b
b in, __name__ = b    # b code execution started
b imports a
a in, __name__ = a    # a code execution started
a imports b           # b code execution is already in progress
b has x True
b has y False         # b defines y after a import,
a out
b out
a in globals() False  # import only adds a to main global symbol table 
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available

Circular imports can be confusing because import does two things:

  1. it executes imported module code
  2. adds imported module to importing module global symbol table

The former is done only once, while the latter at each import statement. Circular import creates situation when importing module uses imported one with partially executed code. In consequence it will not see objects created after import statement. Below code sample demonstrates it.

Circular imports are not the ultimate evil to be avoided at all cost. In some frameworks like Flask they are quite natural and tweaking your code to eliminate them does not make the code better.

main.py

print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
    print 'imports done'
    print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)

b.by

print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"

a.py

print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"

python main.py output with comments

import b
b in, __name__ = b    # b code execution started
b imports a
a in, __name__ = a    # a code execution started
a imports b           # b code execution is already in progress
b has x True
b has y False         # b defines y after a import,
a out
b out
a in globals() False  # import only adds a to main global symbol table 
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available

回答 9

我通过以下方式解决了问题,并且工作正常,没有任何错误。考虑两个文件a.pyb.py

我添加到a.py它,它的工作。

if __name__ == "__main__":
        main ()

a.py:

import b
y = 2
def main():
    print ("a out")
    print (b.x)

if __name__ == "__main__":
    main ()

b.py:

import a
print ("b out")
x = 3 + a.y

我得到的输出是

>>> b out 
>>> a out 
>>> 5

I solved the problem the following way, and it works well without any error. Consider two files a.py and b.py.

I added this to a.py and it worked.

if __name__ == "__main__":
        main ()

a.py:

import b
y = 2
def main():
    print ("a out")
    print (b.x)

if __name__ == "__main__":
    main ()

b.py:

import a
print ("b out")
x = 3 + a.y

The output I get is

>>> b out 
>>> a out 
>>> 5

回答 10

好的,我想我有一个很酷的解决方案。假设您有file a和file b。你有一个def或一个class文件b要在模块来使用a,但你有别的东西,无论是一个defclass或者从文件变量a您在文件中定义或类需要b。您可以做的是,在文件底部a,在调用文件a中所需的文件中b的函数或类之后,但是在从文件b中调用所需的文件中调用函数或类之前a,说import b 然后,这是关键部分,在文件b中所有需要the defor classfrom file 的定义或类中a(叫它CLASS),你说from a import CLASS

之所以可行,是因为您可以导入文件b而无需Python在file中执行任何import语句b,因此可以避免任何循环导入。

例如:

档案a:

class A(object):

     def __init__(self, name):

         self.name = name

CLASS = A("me")

import b

go = B(6)

go.dostuff

文件b:

class B(object):

     def __init__(self, number):

         self.number = number

     def dostuff(self):

         from a import CLASS

         print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."

Ok, I think I have a pretty cool solution. Let’s say you have file a and file b. You have a def or a class in file b that you want to use in module a, but you have something else, either a def, class, or variable from file a that you need in your definition or class in file b. What you can do is, at the bottom of file a, after calling the function or class in file a that is needed in file b, but before calling the function or class from file b that you need for file a, say import b Then, and here is the key part, in all of the definitions or classes in file b that need the def or class from file a (let’s call it CLASS), you say from a import CLASS

This works because you can import file b without Python executing any of the import statements in file b, and thus you elude any circular imports.

For example:

File a:

class A(object):

     def __init__(self, name):

         self.name = name

CLASS = A("me")

import b

go = B(6)

go.dostuff

File b:

class B(object):

     def __init__(self, number):

         self.number = number

     def dostuff(self):

         from a import CLASS

         print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."

Voila.


ImportError:无法导入名称X

问题:ImportError:无法导入名称X

我有四个不同的文件,分别命名为:main,vector,entity和physics。我不会发布所有代码,而只会发布导入代码,因为我认为这就是错误所在。(如果需要,我可以发布更多信息)

主要:

import time
from entity import Ent
from vector import Vect
#the rest just creates an entity and prints the result of movement

实体:

from vector import Vect
from physics import Physics
class Ent:
    #holds vector information and id
def tick(self, dt):
    #this is where physics changes the velocity and position vectors

向量:

from math import *
class Vect:
    #holds i, j, k, and does vector math

物理:

from entity import Ent
class Physics:
    #physics class gets an entity and does physics calculations on it.

然后,我从main.py运行,出现以下错误:

Traceback (most recent call last):
File "main.py", line 2, in <module>
    from entity import Ent
File ".../entity.py", line 5, in <module>
    from physics import Physics
File ".../physics.py", line 2, in <module>
    from entity import Ent
ImportError: cannot import name Ent

我对Python非常陌生,但是已经使用C ++了很长时间。我猜测该错误是由于两次导入实体引起的,一次是在主体中,一次是在物理学中,但是我不知道解决方法。有人可以帮忙吗?

I have four different files named: main, vector, entity and physics. I will not post all the code, just the imports, because I think that’s where the error is. (If you want, I can post more)

Main:

import time
from entity import Ent
from vector import Vect
#the rest just creates an entity and prints the result of movement

Entity:

from vector import Vect
from physics import Physics
class Ent:
    #holds vector information and id
def tick(self, dt):
    #this is where physics changes the velocity and position vectors

Vector:

from math import *
class Vect:
    #holds i, j, k, and does vector math

Physics:

from entity import Ent
class Physics:
    #physics class gets an entity and does physics calculations on it.

I then run from main.py and I get the following error:

Traceback (most recent call last):
File "main.py", line 2, in <module>
    from entity import Ent
File ".../entity.py", line 5, in <module>
    from physics import Physics
File ".../physics.py", line 2, in <module>
    from entity import Ent
ImportError: cannot import name Ent

I am very new to Python but have worked with C++ for a long time. I’m guessing that the error is due to importing entity twice, once in main, and later in physics, but I don’t know a workaround. Can anyone help?


回答 0

您有循环依赖进口。physics.py从定义entity类之前导入,Entphysics尝试导入entity已初始化的类。physicsentity模块中删除对的依赖。

You have circular dependent imports. physics.py is imported from entity before class Ent is defined and physics tries to import entity that is already initializing. Remove the dependency to physics from entity module.


回答 1

虽然您绝对应该避免循环依赖,但可以推迟python中的导入。

例如:

import SomeModule

def someFunction(arg):
    from some.dependency import DependentClass

这(至少在某些情况下)将避免该错误。

While you should definitely avoid circular dependencies, you can defer imports in python.

for example:

import SomeModule

def someFunction(arg):
    from some.dependency import DependentClass

this ( at least in some instances ) will circumvent the error.


回答 2

这是一个循环依赖性。无需对代码进行任何结构修改即可解决。发生此问题的原因是,在vector您要求entity立即可用时,反之亦然。出现此问题的原因是,您要求在模块准备就绪之前通过使用来访问模块的内容from x import y。这基本上与

import x
y = x.y
del x

Python能够检测循环依赖关系并防止导入的无限循环。本质上,所有发生的事情都是为模块创建了一个空的占位符(即,它没有内容)。一旦循环依赖的模块被编译,它将更新导入的模块。这是类似的工作。

a = module() # import a

# rest of module

a.update_contents(real_a)

为了使python能够使用循环依赖关系,您必须import x仅使用样式。

import x
class cls:
    def __init__(self):
        self.y = x.y

由于您不再在顶层引用模块的内容,因此python可以编译模块,而无需实际访问循环依赖项的内容。顶层是指在编译过程中将执行的行,而不是函数的内容(例如y = x.y)。访问模块内容的静态或类变量也会引起问题。

This is a circular dependency. It can be solved without any structural modifications to the code. The problem occurs because in vector you demand that entity be made available for use immediately, and vice versa. The reason for this problem is that you asking to access the contents of the module before it is ready — by using from x import y. This is essentially the same as

import x
y = x.y
del x

Python is able to detect circular dependencies and prevent the infinite loop of imports. Essentially all that happens is that an empty placeholder is created for the module (ie. it has no content). Once the circularly dependent modules are compiled it updates the imported module. This is works something like this.

a = module() # import a

# rest of module

a.update_contents(real_a)

For python to be able to work with circular dependencies you must use import x style only.

import x
class cls:
    def __init__(self):
        self.y = x.y

Since you are no longer referring to the contents of the module at the top level, python can compile the module without actually having to access the contents of the circular dependency. By top level I mean lines that will be executed during compilation as opposed to the contents of functions (eg. y = x.y). Static or class variables accessing the module contents will also cause problems.


回答 3

弄清楚逻辑很重要。出现此问题,因为引用变成死循环。

如果您不想更改逻辑,则可以将导致ImportError的某些import语句放在文件的其他位置,例如结尾。

py

from test.b import b2

def a1():
    print('a1')
    b2()

b.py

from test.a import a1

def b1():
    print('b1')
    a1()

def b2():
    print('b2')

if __name__ == '__main__':
    b1()

您将收到导入错误: ImportError: cannot import name 'a1'

但是,如果我们改变test.b的位置,将b2导入A如下所示:

py

def a1():
    print('a1')
    b2()

from test.b import b2

我们可以得到我们想要的:

b1
a1
b2

To make logic clear is very important. This problem appear, because the reference become a dead loop.

If you don’t want to change the logic, you can put the some import statement which caused ImportError to the other position of file, for example the end.

a.py

from test.b import b2

def a1():
    print('a1')
    b2()

b.py

from test.a import a1

def b1():
    print('b1')
    a1()

def b2():
    print('b2')

if __name__ == '__main__':
    b1()

You will get Import Error: ImportError: cannot import name 'a1'

But if we change the position of from test.b import b2 in A like below:

a.py

def a1():
    print('a1')
    b2()

from test.b import b2

And the we can get what we want:

b1
a1
b2

回答 4

这是一个循环依赖性。我们可以通过在需要的地方使用导入模块或类或函数来解决此问题。如果我们使用这种方法,我们可以修复循环依赖

py

from B import b2
def a1():
    print('a1')
    b2()

py

def b1():
   from A import a1
   print('b1')
   a1()

def b2():
   print('b2')
if __name__ == '__main__':
   b1() 

This is a circular dependency. we can solve this problem by using import module or class or function where we needed. if we use this approach, we can fix circular dependency

A.py

from B import b2
def a1():
    print('a1')
    b2()

B.py

def b1():
   from A import a1
   print('b1')
   a1()

def b2():
   print('b2')
if __name__ == '__main__':
   b1() 

回答 5

我也刚收到这个错误,原因有所不同…

from my_sub_module import my_function

主脚本具有Windows行尾。my_sub_module具有UNIX行结尾。将它们更改为相同可解决此问题。它们还需要具有相同的字符编码。

I just got this error too, for a different reason…

from my_sub_module import my_function

The main script had Windows line endings. my_sub_module had UNIX line endings. Changing them to be the same fixed the problem. They also need to have the same character encoding.


回答 6

如前所述,这是由 循环依赖。尚未提及的是,当您使用Python输入模块并且仅导入用于注释 Types的类时,可以使用 Forward引用

当类型提示包含尚未定义的名称时,该定义可以表示为字符串文字,以便稍后解析。

并删除依赖项(import),例如代替

from my_module import Tree

def func(arg: Tree):
    # code

做:

def func(arg: 'Tree'):
    # code

(注意删除的import语句)

As already mentioned, this is caused by a circular dependency. What has not been mentioned is that when you’re using Python typing module and you import a class only to be used to annotate Types, you can use Forward references:

When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.

and remove the dependency (the import), e.g. instead of

from my_module import Tree

def func(arg: Tree):
    # code

do:

def func(arg: 'Tree'):
    # code

(note the removed import statement)


回答 7

不要使用您导入的其他模块的名称来命名当前的python脚本

解决方案:重命名工作的python脚本

例:

  1. 你在工作 medicaltorch.py
  2. 在该脚本中,您具有:应该from medicaltorch import datasets as mt_datasets在哪里medicaltorch安装了模块

将会失败ImportError。只需在1中重命名工作的python脚本即可。

Don’t name your current python script with the name of some other module you import

Solution: rename your working python script

Example:

  1. you are working in medicaltorch.py
  2. in that script, you have: from medicaltorch import datasets as mt_datasets where medicaltorch is supposed to be an installed module

This will fail with the ImportError. Just rename your working python script in 1.


回答 8

在这里还没有看到这个-这真是愚蠢,但是请确保您导入的是正确的变量/函数。

我收到此错误

ImportError:无法导入名称IMPLICIT_WAIT

因为我的变量实际上是IMPLICIT_TIMEOUT

当我将导入更改为使用正确的名称时,我不再收到错误🤦‍♂️

Don’t see this one here yet – this is incredibly stupid, but make sure you’re importing the correct variable/function.

I was getting this error

ImportError: cannot import name IMPLICIT_WAIT

because my variable was actually IMPLICIT_TIMEOUT.

when I changed my import to use the correct name, I no longer got the error 🤦‍♂️


回答 9

如果file1.py要从中导入file2.py并使用此功能:

if __name__ == '__main__':
    # etc

低于in的变量file1.py 不能导入file2.py因为__name__ 不相等 __main__

如果你想从进口的东西file1.pyfile2.py,你需要使用此file1.py

if __name__ == 'file1':
    # etc

如有疑问,请发表assert声明以确定是否__name__=='__main__'

If you are importing file1.py from file2.py and used this:

if __name__ == '__main__':
    # etc

Variables below that in file1.py cannot be imported to file2.py because __name__ does not equal __main__!

If you want to import something from file1.py to file2.py, you need to use this in file1.py:

if __name__ == 'file1':
    # etc

In case of doubt, make an assert statement to determine if __name__=='__main__'


回答 10

跟踪导入错误的一种方法是逐步尝试在每个导入的文件上运行python以跟踪不良文件。

  1. 你会得到类似:

    python ./main.py

    ImportError:无法导入名称A

  2. 然后您启动:

    python ./modules/a.py

    ImportError:无法导入名称B

  3. 然后您启动:

    python ./modules/b.py

    ImportError:无法导入名称C(某些不存在的模块或某些其他错误)

One way to track import error is step by step trying to run python on each of imported files to track down bad one.

  1. you get something like:

    python ./main.py
    

    ImportError: cannot import name A

  2. then you launch:

    python ./modules/a.py
    

    ImportError: cannot import name B

  3. then you launch:

    python ./modules/b.py
    

    ImportError: cannot import name C (some NON-Existing module or some other error)


回答 11

同样与OP没有直接关系,但是在向模块添加新对象后未能重新启动 PyCharm Python控制台也是一种使人非常困惑的好方法ImportError: Cannot import name ...

令人困惑的是,PyCharm 在控制台中自动完成导入,但是导入失败。

Also not directly relevant to the OP, but failing to restart a PyCharm Python console, after adding a new object to a module, is also a great way to get a very confusing ImportError: Cannot import name ...

The confusing part is that PyCharm will autocomplete the import in the console, but the import then fails.


回答 12

在我的情况下,我正在使用Jupyter笔记本工作,而发生这种情况的原因是,从我在工作文件中定义类/函数起,就已经缓存了导入。

我重新启动了Jupyter内核,错误消失了。

In my case, I was working in a Jupyter notebook and this was happening due the import already being cached from when I had defined the class/function inside my working file.

I restarted my Jupyter kernel and the error disappeared.


回答 13

不是专门针对此问询者,但是如果您导入中的类名与您要从中导入的文件中的定义不匹配,则会显示此相同错误。

Not specifically for this asker, but this same error will show if the class name in your import doesn’t match the definition in the file you’re importing from.


回答 14

问题很明显:和模块中的名称之间存在循环依赖关系。entityphysics

无论导入整个模块还是仅导入类,都必须加载名称。

观看此示例:

# a.py
import b
def foo():
  pass
b.bar()
# b.py
import a
def bar():
  pass
a.foo()

它将被编译成:

# a.py
# import b
# b.py
# import a # ignored, already importing
def bar():
  pass
a.foo()
# name a.foo is not defined!!!
# import b done!
def foo():
  pass
b.bar()
# done!

稍作更改,我们可以解决此问题:

# a.py
def foo():
  pass
import b
b.bar()
# b.py
def bar():
  pass
import a
a.foo()

它将被编译成:

# a.py
def foo():
  pass
# import b
# b.py
def bar():
  pass
# import a # ignored, already importing
a.foo()
# import b done!
b.bar()
# done!

The problem is clear: circular dependency between names in entity and physics modules.

Regardless of importing the whole module or just a class, the names must be loaded .

Watch this example:

# a.py
import b
def foo():
  pass
b.bar()
# b.py
import a
def bar():
  pass
a.foo()

This will be compiled into:

# a.py
# import b
# b.py
# import a # ignored, already importing
def bar():
  pass
a.foo()
# name a.foo is not defined!!!
# import b done!
def foo():
  pass
b.bar()
# done!

With one slight change we can solve this:

# a.py
def foo():
  pass
import b
b.bar()
# b.py
def bar():
  pass
import a
a.foo()

This will be compiled into:

# a.py
def foo():
  pass
# import b
# b.py
def bar():
  pass
# import a # ignored, already importing
a.foo()
# import b done!
b.bar()
# done!