导入__init__.py和`import as`语句

and*_*and 19 python python-3.x

我遇到了在包的模块中导入__init__.py和使用import as绝对导入的问题.

我的项目有一个子包,在其中__init__.py我将一个类从一个模块"提升"到带有from import as语句的子包级别.该模块使用绝对导入从该子包导入其他模块.我收到这个错误AttributeError: 'module' object has no attribute 'subpkg'.

结构:

pkg/
??? __init__.py
??? subpkg
?   ??? __init__.py
?   ??? one.py
?   ??? two_longname.py
??? tst.py
Run Code Online (Sandbox Code Playgroud)

pkg/__ init__.py为空.

pkg/subpkg/__ init__.py:

from pkg.subpkg.one import One
Run Code Online (Sandbox Code Playgroud)

pkg/subpkg/one.py:

import pkg.subpkg.two_longname as two

class One(two.Two):
    pass
Run Code Online (Sandbox Code Playgroud)

pkg/subpkg/two_longname.py:

class Two:
    pass
Run Code Online (Sandbox Code Playgroud)

pkg/tst.py:

from pkg.subpkg import One

print(One)
Run Code Online (Sandbox Code Playgroud)

输出:

$ python3.4 -m pkg.tst
Traceback (most recent call last):
  File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/and/dev/test/python/imptest2/pkg/tst.py", line 1, in <module>
    from pkg.subpkg import One
  File "/home/and/dev/test/python/imptest2/pkg/subpkg/__init__.py", line 1, in <module>
    from pkg.subpkg.one import One
  File "/home/and/dev/test/python/imptest2/pkg/subpkg/one.py", line 1, in <module>
    import pkg.subpkg.two_longname as two
AttributeError: 'module' object has no attribute 'subpkg'
Run Code Online (Sandbox Code Playgroud)

解决方法

有些变化使其有效:

  1. 清空pkg/subpkg/__init__.py并直接导入pkg.subpkg.one.

    我不认为这是一个选项,因为AFAIK"提升"到包级别的东西是可以的.这是一篇文章的引用:

    init .py中要做的一件事是将选定的类,函数等导入到包级别中,以便可以方便地从包中导入它们.

  2. 更改__init__.pyimport asfrom import:

    from pkg.subpkg import two_longname
    
    class One(two_longname.Two):
        pass
    
    Run Code Online (Sandbox Code Playgroud)

    这里唯一的问题是我不能为模块创建一个简短的别名.我从@ begueradj的回答中得到了这个想法.

也可以使用相对导入one.py来解决问题.但我认为这只是变通方法#2的变种.

问题

  1. 有人能解释一下这里到底发生了什么吗?为什么进口one.py和使用的组合__init__.py导致了这样的问题?

  2. 有没有更好的解决方法?


原始的例子

这是我最初的例子.这不是很现实,但我没有删除它所以@ begueradj的答案仍然有意义.

pkg/__ init__.py为空.

pkg/subpkg/__ init__.py:

from pkg.subpkg.one import ONE
Run Code Online (Sandbox Code Playgroud)

pkg/subpkg/one.py:

import pkg.subpkg.two
ONE = pkg.subpkg.two.TWO
Run Code Online (Sandbox Code Playgroud)

pkg/subpkg/two.py:

TWO = 2
Run Code Online (Sandbox Code Playgroud)

pkg/tst.py:

from pkg.subpkg import ONE
Run Code Online (Sandbox Code Playgroud)

输出:

$ python3.4 -m pkg.tst
Traceback (most recent call last):
  File "/usr/lib/python3.4/runpy.py", line 170, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/home/and/dev/test/python/imptest/pkg/tst.py", line 1, in <module>
    from pkg.subpkg import ONE
  File "/home/and/dev/test/python/imptest/pkg/subpkg/__init__.py", line 2, in <module>
    from pkg.subpkg.one import ONE
  File "/home/and/dev/test/python/imptest/pkg/subpkg/one.py", line 6, in <module>
    ONE = pkg.subpkg.two.TWO
AttributeError: 'module' object has no attribute 'subpkg'
Run Code Online (Sandbox Code Playgroud)

最初我在one.py中有这个:

import pkg.subpkg.two as two
ONE = two.TWO
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我在导入时遇到错误(就像在我使用的原始项目中一样import as).

Ant*_*ala 14

你错误地认为一个人不能拥有别名from ... import,就像from ... import ... asPython 2.0以来一样.这import ... as是一个不为人知的晦涩语法,但是你在代码中偶然使用的语法.

PEP 0221声称以下2个"有效"相同:

  1. import foo.bar.bazaar as baz
  2. from foo.bar import bazaar as baz

声明并不完全正确,正如您遇到的极端情况所证明的那样,即所需的模块已经存在sys.modules但尚未初始化.的import ... as要求,该模块foo.bar被注入foo一个命名空间属性bar,除了是在sys.modules,而from ... import ... as验看foo.barsys.modules.

(还要注意,import foo.bar只能确保模块foo.bar处于sys.modules可访问状态foo.bar,但可能尚未完全初始化.)

如下更改代码对我来说是个窍门:

# import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname as two
Run Code Online (Sandbox Code Playgroud)

代码在Python 2和Python 3上运行完美.

另外,出于同样的原因,one.py你无法做到from pkg import subpkg.


要进一步演示此错误,请one.py按上述方法修复,并在以下代码中添加以下代码tst.py:

import pkg
import pkg.subpkg.two_longname as two

del pkg.subpkg

from pkg.subpkg import two_longname as two
import pkg.subpkg.two_longname as two
Run Code Online (Sandbox Code Playgroud)

只有最后一行崩溃,因为from ... import在咨询sys.modulespkg.subpkg,发现它在那里,而import ... as咨询sys.modulespkg,并试图找出subpkg为在属性pkg模块.由于我们刚刚删除了该属性,因此最后一行失败了AttributeError: 'module' object has no attribute 'subpkg'.


由于import foo.bar as baz语法有点模糊,并且添加了更多的极端情况,并且我很少见到它被使用过,我建议完全避免使用它from .. import ... as.

  • 这已在Python 3.7中修复,请参阅问题[#23203](https://bugs.python.org/issue23203)和[#30024](https://bugs.python.org/issue30024). (6认同)