python - 导入 __init__.py 和 'import as' 语句

标签 python python-3.x python-import

我在 __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

pkg/init.py 是空的。

pkg/subpkg/init.py:

from pkg.subpkg.one import One

pkg/subpkg/one.py:

import pkg.subpkg.two_longname as two

class One(two.Two):
    pass

pkg/subpkg/two_longname.py:

class Two:
    pass

pkg/tst.py:

from pkg.subpkg import One

print(One)

输出:

$ 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'

解决方法

有使其工作的变化:

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

    我不认为这是一个选项,因为 AFAIK 将东西“提升”到包级别是可以的。这是引自 an article :

    One common thing to do in your __init__.py is to import selected Classes, functions, etc into the package level so they can be conveniently imported from the package.

  2. one.py 中将 import as 更改为 from import:

     from pkg.subpkg import two_longname
    
     class One(two_longname.Two):
         pass
    

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

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

问题

  1. 有人可以解释这里到底发生了什么吗?为什么 __init__.py 中的导入和 import as 的使用组合会导致此类问题?

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


原始示例

这是我原来的例子。这不是很现实,但我不会删除它,所以@begueradj 的回答仍然有意义。

pkg/init.py 是空的。

pkg/subpkg/init.py:

from pkg.subpkg.one import ONE

pkg/subpkg/one.py:

import pkg.subpkg.two
ONE = pkg.subpkg.two.TWO

pkg/subpkg/two.py:

TWO = 2

pkg/tst.py:

from pkg.subpkg import ONE

输出:

$ 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'

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

import pkg.subpkg.two as two
ONE = two.TWO

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

最佳答案

您错误地认为不能使用 from ... import 别名,因为 from ... import ... as 自 Python 2.0 以来一直存在。 import ... as 是一种鲜为人知的语法,但您在代码中无意中使用了它。

PEP 0221声称以下 2 个“有效地”相同:

  1. 将 foo.bar.bazaar 导入为 baz
  2. from foo.bar import bazaar as baz

该声明在 Python 3.6.x 及之前的版本中并不完全正确,您遇到的极端情况证明了这一点,即如果所需的模块已经存在于 sys.modules 但尚未初始化。 import ... as 要求将模块 foo.bar 作为属性 bar 注入(inject)到 foo 命名空间中,除了在 sys.modules 中,而 from ... import ... as 中查找 foo.bar >sys.modules.

(另请注意,import foo.bar 仅确保模块 foo.barsys.modules 中并且可作为 foo.bar,但可能还没有完全初始化。)

如下更改代码对我有用:

# import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname as two

而且代码在 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

只有最后一行崩溃,因为 from ... importpkg.subpkg 查询 sys.modules 并在那里找到它,而 import ... aspkg 查询 sys.modules 并尝试在pkg 模块。由于我们刚刚删除了该属性,最后一行失败并显示 AttributeError: 'module' object has no attribute 'subpkg'


由于 import foo.bar as baz 语法有点晦涩,而且添加了更多极端情况,而且我很少看到它被使用,我建议完全避免使用它并支持 from .. import ... as.

关于python - 导入 __init__.py 和 'import as' 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57090921/

相关文章:

python - 禁止 Python 向磁盘写入任何内容

python - 检查包含超过 64 个字符的数据框列

python - 哪些类不能被子类化?

python-3.x - Bigquery 无法将输入字符串解析为 TIMESTAMP

python-3.x - 如何使Docker容器与本地主机上的geth对话

python - 安装包后出现 'module not found' 错误

Python 相对/绝对导入(再次)

python - 如何将 ipywidget slider 的默认位置更改为 matplotlib 图的一侧?

python - 计算表中每 x 行的平均值并创建新表

Python 模块 : functions calling each other