【问题标题】:Imports in __init__.py and 'import as' statement在 __init__.py 和 'import as' 语句中导入
【发布时间】:2014-09-08 13:18:41
【问题描述】:

我在 __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的引用:

    在您的__init__.py 中要做的一件常见事情是导入选定的 将类、函数等放入包级别,以便它们可以 方便地从包中导入。

  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)。

【问题讨论】:

  • 我再次阅读了您的代码,逐个案例。我发现你错过了一个真实的事实:tst.py 位于 pkg 本身之下,所以每次你从任何你想要的地方调用import pkg.subpkg.SOMETHINGfrom pkg.subpkg import SOMETHING 时,你都会得到同样的错误。我重新编辑了我的答案:我只更改了您程序的结构,主要是 tst.pypkg 之外(实际上,自从我第一次发布以来,我就这样运行它我的回答如下)。如果您想将 tst.py 保留在 pkg 中,请从您的所有呼叫中删除 pgk,例如 import pkg.subpkg.SOMETHINGfrom pkg.subpkg import SOMETHING
  • tst.py 在包里面,就是这样。 “删除pkg你所有的电话......”是什么意思?相对进口?我知道它适用于相对进口。问题是为什么它不能像现在这样工作。
  • 我的意思是你需要运行import subpkg.SOMETHING 而不是import pkg.subpkg.SOMETHINGfrom subpkg import SOMETHING 而不是from pkg.subpkg import SOMETHING。为什么 ?好吧:你不能用无效的路径运行你的程序。就这么简单。

标签: python python-3.x python-import


【解决方案1】:

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

PEP 0221 声称以下两个“实际上”相同:

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

3.6.x 及之前的 Python 版本 中,该陈述并不完全正确,正如您遇到的极端情况所证明的那样,即如果所需的模块已存在于 sys.modules 中但尚未初始化. import ... as 要求将模块 foo.bar 作为属性 bar 注入到 foo 命名空间中,此外还要在 sys.modules 中,而 from ... import ... assys.modules 中查找 foo.bar

(另请注意,import foo.bar 仅确保模块 foo.bar 位于 sys.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 并试图找到subpkg 作为属性pkg 模块。由于我们刚刚删除了该属性,因此最后一行以 AttributeError: 'module' object has no attribute 'subpkg' 失败。


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

【讨论】:

  • 此问题现已在 Python 3.7 中得到修复,请参阅问题 #23203#30024
  • 可能值得注意的是,import pkg.subpkg.two_longname 确实有效,但 import pkg.subpkg.two_longname as two 在 python import ... vs from ... import ...引起的;在 python 3.7 中,两者都可以工作;问题 30024 很好地涵盖了这一点;
【解决方案2】:

这是一个关于发生了什么的理论。

当你使用as保留字时,例如:

import pkg.subpkg.two_longname as two

Python 必须完全初始化和解决与pkg.subpkg 相关的所有依赖项。但是有一个问题,要完全加载subpkg,你也需要完全加载one.py,对吗?同时使用as 关键字导入two_longname.py ...你能看到这里的递归吗?这就是为什么在做的那一刻:

import pkg.subpkg.two_longname as two

您收到一个错误,声称 subpkg 不存在。

要执行测试,请转到 one.py 并将其更改为:

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

#class One(two.Two):
class One(two_longname.Two):
    pass

我想这都是关于性能的,Python 尽可能地加载一个模块。 as 关键字是例外之一。我不知道是否还有其他人,但了解他们会很有趣。

【讨论】:

    【解决方案3】:

    正如接受的答案所述,这是 Python 行为的问题。

    我提交了一个错误:http://bugs.python.org/issue30024

    Serhiy Storchaka 的修复已合并并预期在 Python 3.7 中

    【讨论】:

      【解决方案4】:

      关于调用模块的方式,您的项目结构必须是这样的:

      pkg/
      ├── __init__.py
      ├── subpkg
      │   ├── __init__.py
      │   ├── one.py
      │   └── two.py
      tst.py
      

      像这样定义你的 two.py

      class TWO:
          def functionTwo(self):
              print("2")
      

      像这样定义你的 one.py

      from pkg.subpkg import two
      class ONE:
          def functionOne(self):
              print("1")
      
              self.T=two.TWO()
              print("Calling TWO from ONE: ")
              self.T.functionTwo()
      

      像这样定义你的 test.py

      from pkg.subpkg import one
      class TEST:
          def functionTest(self):
              O=one.ONE()
              O.functionOne()
      if __name__=='__main__':
          T=TEST()
          T.functionTest()
      

      当你执行时,你会得到这个:

      1
      Calling  TWO from  ONE:
      2
      

      希望这会有所帮助。

      【讨论】:

      • 我的理解是,将导入的东西的使用转移到函数中会有所帮助,因为它允许 python 首先导入所有内容,然后开始使用它。然而,这并不总是可以做到的。例如 - 导入超类。也许我应该把我的例子建立在那个案例上,让它更真实。
      • 我还注意到您使用from pkg.subpkg import two 而不是import pkg.subpkg.two as two。这件事似乎使我原来的例子工作。我会调查的。谢谢!
      • 我认为使用__init__.py 使类、函数等在包级别可用是绝对可以的。
      猜你喜欢
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-30
      • 2017-03-12
      • 2023-03-04
      • 1970-01-01
      相关资源
      最近更新 更多