使用绝对导入和__init__.py时是否为循环导入

时间:2016-05-17 13:50:13

标签: python python-import

我有以下文件结构,每个文件结构最多包含一行代码(如下所示):

a
├── b
│   ├── c.py          import a.b.d as d
│   ├── d.py
│   └── __init__.py   from a.b.c import *
├── __init__.py
└── main.py           import a.b as b

通过运行python -m a.main,我收到以下错误:

Traceback (most recent call last):
  File "/usr/lib/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/tmp/test/a/main.py", line 1, in <module>
    import a.b as b
  File "a/b/__init__.py", line 1, in <module>
    from a.b.c import *
  File "a/b/c.py", line 1, in <module>
    import a.b.d as d
AttributeError: 'module' object has no attribute 'b'

我不确定这是否是由循环导入引起的。如果我将import a.b.d as d更改为from a.b import d,则不会再出现任何错误。

1 个答案:

答案 0 :(得分:2)

AttributeError是由文件as中导入语句中的c.py引起的。

整个过程如下:

  1. main.py创建了模块a,将其添加到sys.modules并对其进行了初始化;
  2. main.py创建了模块a.b,将其添加到sys.modules并开始执行其代码;
  3. b/__init__.pyaa.b已在sys.modules)已创建的模块a.b.c中,已将其添加到sys.modules并开始执行代码;
  4. b/c.py创建了模块a.b.d,将其添加到sys.modules,执行了代码,将其添加为模块a.b的属性“d”,然后尝试但未通过将a.b.d绑定到名称d。问题是模块a.b尚未初始化,因此属性'b'不在模块a中。
  5. <强> WHY

    要理解这一点,您应该知道import语句可以执行两项操作(Python 2Python 3)。

    1. 查找一个或多个模块,并在必要时对其进行初始化;
    2. 在本地命名空间中定义名称并将其绑定到某个模块。
    3. 模块查找

      前一个调用__import__挂钩,它加载模块并初始化它。在Python 2中,默认情况下挂钩为imputil.ImportManager._import_hook,它的工作原理如下。

      1. 检查模块是否在sys.modules;
      2. 如果没有,找到模块并获取其代码;
      3. 创建模块并将其添加到sys.modules;
      4. 在模块的命名空间中运行代码;
      5. 退回模块。
      6. 如果语句类似import a.b.c,模块查找过程将递归查找模块aa.ba.b.c,并在sys.modules中跟踪它们}。如果返回模块a.b,则将其设置为模块a的属性“b”。最后,模块查找过程将返回顶层模块a

        如果陈述与from a.b import c,d相同,则结果略有不同。在这种情况下,将返回底部模块(即模块a.b)。

        名称绑定

        如果使用import [module]语句,顶层​​模块的名称将绑定到返回值(这是顶层模块)。

        如果使用import [module] as [name]语句,则[name]将绑定到底层模块(通过访问顶层模块的属性)。

        如果使用from [module] import [identifier],那么底部模块的名称将绑定到返回值(from import语句中的底部模块)。

        Example
        import a.b.c          # a <- <module 'a'>
        import a.b.c as c     # c <- <module 'a'>.b.c
        from a.b import c     # c <- <module 'a.b'>.c
        

        在您的问题中,c.py中的import语句在模块a.b被初始化一半并且尚未在模块a的属性中注册时发生。因此import asa.b.c绑定到c会遇到问题。但是,由于模块a.b已在sys.modules中注册,因此使用from import不会遇到此类问题。