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

Python中的循环(或循环)导入,python,circular-dependency,cyclic-reference,Python,Circular Dependency,Cyclic Reference,如果两个模块相互导入,会发生什么情况 一般来说,Python中的循环导入如何?去年对此进行了很好的讨论。它非常彻底地回答了你的问题 进口真的很简单。只要记住以下几点: $ python a.py a in b imported: False b in a in b imported: True a out b out a out “import”和“from xxx import yyy”是可执行语句。他们执行 当运行的程序到达该行时 如果模块不在sys.modules中,则导入将创建新模块

如果两个模块相互导入,会发生什么情况


一般来说,Python中的循环导入如何?

去年对此进行了很好的讨论。它非常彻底地回答了你的问题

进口真的很简单。只要记住以下几点:

$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out
“import”和“from xxx import yyy”是可执行语句。他们执行 当运行的程序到达该行时

如果模块不在sys.modules中,则导入将创建新模块 输入sys.modules,然后执行模块中的代码。事实并非如此 将控制权返回到调用模块,直到执行完成

如果sys.modules中确实存在模块,则导入只返回该模块 模块是否已完成执行。这就是原因 循环导入可能返回部分为空的模块

最后,执行脚本在名为uuu main_uuu的模块中运行,导入 该脚本将以其自己的名称创建一个与之无关的新模块 __主要问题

把这些东西放在一起,你在导入时不应该有任何惊喜 模块


循环导入终止,但在模块初始化期间,需要小心不要使用循环导入的模块

考虑以下文件:

a、 py:

b、 py:

如果执行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中),Python解释器不会再次导入
b
,因为它已经存在于模块dict中

如果在模块初始化期间尝试从
a
访问
b.x
,您将获得
AttributeError

将以下行附加到
a.py

print b.x
from src.main import Foo

class SpecificException(Exception):
    def __init__(self, cause: Foo):
        self.cause = cause

    def __str__(self):
        return f'Expected 3 but got {self.cause.attrib}.'
然后,输出为:

$ 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'
$python a.py
a在
b:错
b英寸
a在
b:是的
出局
回溯(最近一次呼叫最后一次):
文件“a.py”,第4行,在
进口b
文件“/home/shlomme/tmp/x/b.py”,第2行,在
导入
文件“/home/shlomme/tmp/x/a.py”,第7行,在
打印b.x
AttributeError:“模块”对象没有属性“x”

这是因为模块是在导入时执行的,在访问
b.x
时,行
x=3
尚未执行,这只会发生在
b out

之后,如果执行
导入foo
(内部
bar.py
)和
导入bar
(内部
foo.py
),则工作正常。当任何东西实际运行时,这两个模块都将被完全加载,并且将具有彼此的引用


问题是当您从foo导入abc
(内部
bar.py
)和从bar导入xyz(内部
foo.py
)执行
操作时。因为现在每个模块都要求在导入之前已经导入另一个模块(这样我们导入的名称就存在了)。

我在这里得到了一个让我印象深刻的示例

foo.py

import bar

class gX(object):
    g = 10
from foo import gX

o = gX()
import foo
import bar

print "all done"
import a
print("This is from module b")
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)
from a import A

class B:
    @staticmethod
    def do_something_b_ish(param):
        A.save_result(B.use_param_like_b_would(param))
def save_result(result):
    print('save the result')
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)
from c import save_result

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

import bar

class gX(object):
    g = 10
from foo import gX

o = gX()
import foo
import bar

print "all done"
import a
print("This is from module b")
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)
from a import A

class B:
    @staticmethod
    def do_something_b_ish(param):
        A.save_result(B.use_param_like_b_would(param))
def save_result(result):
    print('save the result')
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)
from c import save_result

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

import bar

class gX(object):
    g = 10
from foo import gX

o = gX()
import foo
import bar

print "all done"
import a
print("This is from module b")
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)
from a import A

class B:
    @staticmethod
    def do_something_b_ish(param):
        A.save_result(B.use_param_like_b_would(param))
def save_result(result):
    print('save the result')
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)
from c import save_result

class B:
    @staticmethod
    def do_something_b_ish(param):
        save_result(B.use_param_like_b_would(param))
在命令行:$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
回溯(最近一次呼叫最后一次):
文件“m.py”,第1行,在
进口食品
文件“/home/xolve/foo.py”,第1行,在
进口栏
文件“/home/xolve/bar.py”,第1行,在
来自foo import gX
导入错误:无法导入名称gX

好的,我想我有一个非常酷的解决方案。 假设您有文件
a
和文件
b
。 您想在模块
a
中使用文件
b
中的
def
class
,但您还需要在文件
b
中的定义或类中使用的
def
class
或文件
a
中的变量。 您可以做的是,在文件
a
的底部,在调用文件
a
中文件
b
中所需的函数或类之后,但在从文件
b
中调用文件
a
所需的函数或类之前,说
import b

然后,这里是关键部分,在文件
b
中所有需要
def
或文件
a
中的
class
(我们称之为
class
)的定义或类中,您可以说是导入类中的

这是因为您可以导入文件
b
,而无需Python执行文件
b
中的任何导入语句,因此可以避免任何循环导入

例如:

文件a: 文件b:
瞧。

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

def dostuff(self):
     from foo import bar
     ...
try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
这将避免在其他模块导入文件时执行import语句。只有当存在逻辑循环依赖时,此操作才会失败

大多数循环导入实际上不是逻辑循环导入,而是引发
importorror
错误,因为
import()
在调用时计算整个文件的顶级语句的方式

如果您确实希望您的导入位于顶部,则几乎可以避免这些
导入程序

以这种循环输入为例:

应用程序A 应用程序B 来自David Beazley的精彩演讲,
1:54:00
,这里有一种在python中处理循环导入的方法:

def dostuff(self):
     from foo import bar
     ...
try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
这将尝试导入
SimplifiedImageSerializer
,如果由于已导入而引发了
ImportError
,它将从importcache中提取它


附言:你必须用大卫·比兹利的声音阅读整篇文章。

我完全同意蟒蛇侠的回答。但我偶然发现了一些代码,这些代码在循环导入中存在缺陷,并在尝试添加单元测试时引发了问题。因此,要在不更改任何内容的情况下快速修补,您可以通过执行动态导入来解决问题

# 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")
再说一次,这不是一个永久的解决办法,但可能会帮助一些人