代码之家  ›  专栏  ›  技术社区  ›  Xolve

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

  •  276
  • Xolve  · 技术社区  · 16 年前

    如果两个模块互相导入会发生什么?

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

    11 回复  |  直到 6 年前
        1
  •  239
  •   Martijn Pieters    12 年前

    关于这件事在 comp.lang.python 去年。它很彻底地回答了你的问题。

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

    “import”和“from xxx import yyy”是可执行语句。他们执行 当运行程序到达该行时。

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

    如果一个模块确实存在于sys.modules中,那么导入只会返回 模块是否已完成执行。这就是为什么 循环导入可能返回部分为空的模块。

    最后,执行脚本在名为uu main_uuu的模块中运行,导入 脚本以自己的名称将创建一个与 第二种。

    把那批货放在一起,进口时不应该有任何意外。 模块。

        2
  •  231
  •   user2357112    8 年前

    如果你这样做了 import foo 里面 bar import bar 里面 foo ,会很好的。当任何东西实际运行时,两个模块都将被完全加载,并且彼此都有引用。

    问题是你什么时候做的 from foo import abc from bar import xyz . 因为现在每个模块都要求在导入之前已经导入另一个模块(以便我们导入的名称存在)。

        3
  •  94
  •   the Tin Man    8 年前

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

    考虑以下文件:

    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 .

        4
  •  25
  •   Sebastian Wozny    9 年前

    正如其他答案所描述的,这种模式在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()
    

    大卫·比兹利的精彩演讲 Modules and Packages: Live and Let Die! - PyCon 2015 , 1:54:00 ,下面是一种在python中处理循环导入的方法:

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

    这将尝试导入 SimplifiedImageSerializer 如果 进口恐怖 引发,因为它已导入,它将从importcache中提取它。

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

        5
  •  8
  •   Mohith Xolve    10 年前

    我有一个让我震惊的例子!

    P.Py

    import bar
    
    class gX(object):
        g = 10
    

    巴利

    from foo import gX
    
    o = gX()
    

    Me.Py

    import foo
    import bar
    
    print "all done"
    

    在命令行: $python主.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
    
        6
  •  4
  •   radtek    8 年前

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

    # 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")
    

    同样,这不是一个永久性的修复,但可以帮助那些希望在不更改过多代码的情况下修复导入错误的人。

    干杯!

        7
  •  1
  •   Jacek Błocki    6 年前

    循环导入可能会混淆,因为导入有两个功能:

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

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

    循环进口并不是不惜一切代价避免的最终祸害。在一些框架中,比如flask,它们是非常自然的,调整代码以消除它们并不能使代码更好。

    Me.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.通过

    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输出

    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
    
        8
  •  1
  •   Mohsen Haddadi    6 年前

    模块ApPy:

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

    模块BY

    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的第一行是 进口B 但是 请注意,此行将不再执行 因为python中的每个文件只执行一次导入行,所以无论何时何地执行导入行都不重要。所以它将传递到下一行并打印 "This is from module a" .
    4. 从模块B访问完整个模块A后,我们仍在模块B,因此下一行将打印 "This is from module b"
    5. 模块B线路完全执行。因此,我们将回到模块A,从那里开始模块B。
    6. 导入B行已执行,将不再执行。下一行将打印 “来自模块A” 程序将完成。
        9
  •  1
  •   Irfanuddin    6 年前

    我用下面的方法解决了这个问题,它工作得很好,没有任何错误。 考虑两个文件 a.py b.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
    
        10
  •  -1
  •   Usman Hussain    6 年前

    这可能是另一个解决方案,对我有效。

    def MandrillEmailOrderSerializer():
    from sastaticketpk.apps.flights.api.v1.serializers import MandrillEmailOrderSerializer
    return MandrillEmailOrderSerializer
    
    email_data = MandrillEmailOrderSerializer()(order.booking).data
    
        11
  •  -2
  •   das-g DuXeN0N    10 年前

    好吧,我想我有一个很酷的解决方案。 假设你有档案 a 文件 b . 你有一个 def 或A class 在文件中 在模块中使用的 但是你还有别的东西,或者 DEF , 或文件中的变量 在定义或文件中的类中需要的 . 你能做的是,在文件的底部 ,在调用文件中的函数或类之后 这是档案中需要的 ,但在从文件调用函数或类之前 你需要的文件 import b 那么,这就是 关键部件 ,在文件中的所有定义或类中 那需要 DEF 从文件 (我们称之为 CLASS 你说 from a import CLASS

    这是因为您可以导入文件 不使用python执行文件中的任何import语句 因此,你可以避开任何循环进口。

    例如:

    文件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."
    

    沃伊拉