首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Imports in __init__.py和'import as‘语句

Imports in __init__.py和'import as‘语句
EN

Stack Overflow用户
提问于 2014-07-17 23:15:06
回答 4查看 10.5K关注 0票数 25

我遇到了在__init__.py中使用imports以及在包的模块中使用import as和绝对imports的问题。

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

示例

Structure

代码语言:javascript
复制
pkg/
├── __init__.py
├── subpkg
│   ├── __init__.py
│   ├── one.py
│   └── two_longname.py
└── tst.py

pkg/init.py为空。

pkg/subpkg/init.py

代码语言:javascript
复制
from pkg.subpkg.one import One

pkg/subpkg/one.py

代码语言:javascript
复制
import pkg.subpkg.two_longname as two

class One(two.Two):
    pass

pkg/subpkg/two_longname.py

代码语言:javascript
复制
class Two:
    pass

pkg/tst.py

代码语言:javascript
复制
from pkg.subpkg import One

print(One)

输出

代码语言:javascript
复制
$ 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. Empty pkg/subpkg/__init__.py并直接从pkg.subpkg.one导入。

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

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

  1. one.py中将import as更改为from import

从pkg.subpkg导入two_longname第一类(two_longname.Two):pass

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

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

问题

  1. ,有人能解释一下到底是怎么回事吗?为什么__init__.py中的导入和import as的使用相结合会导致这样的问题?

还有更好的workarounds?吗?

原始示例

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

pkg/init.py为空。

pkg/subpkg/init.py

代码语言:javascript
复制
from pkg.subpkg.one import ONE

pkg/subpkg/one.py

代码语言:javascript
复制
import pkg.subpkg.two
ONE = pkg.subpkg.two.TWO

pkg/subpkg/two.py

代码语言:javascript
复制
TWO = 2

pkg/tst.py

代码语言:javascript
复制
from pkg.subpkg import ONE

输出

代码语言:javascript
复制
$ 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中有以下内容

代码语言:javascript
复制
import pkg.subpkg.two as two
ONE = two.TWO

在这种情况下,我会在导入时出错(就像我的原始项目一样,它也使用import as )。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-07-26 16:07:09

您错误地认为不能使用from ... import作为别名,因为from ... import ... as从Python2.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.barfoo名称空间中作为属性bar注入,而from ... import ... as则在sys.modules中查找foo.bar

(还要注意,import foo.bar仅确保模块foo.bar位于sys.modules中并可作为foo.bar访问,但可能尚未完全初始化。)

按如下所示更改代码为我做了相应的工作:

代码语言:javascript
复制
# import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname as two

代码可以在Python2和Python3上完美运行。

此外,由于同样的原因,在one.py中您不能执行from pkg import subpkg

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

代码语言:javascript
复制
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 ... importsys.modules中查询pkg.subpkg并在那里找到它,而import ... assys.modules中查询pkg并尝试将subpkg作为pkg模块中的一个属性。因为我们刚刚删除了该属性,所以最后一行将失败,并显示AttributeError: 'module' object has no attribute 'subpkg'

由于import foo.bar as baz语法有点晦涩难懂,并且添加了更多的角例,而且我很少看到它被使用,所以我建议完全避免使用它,并倾向于使用from .. import ... as

票数 18
EN

Stack Overflow用户

发布于 2014-07-23 21:50:56

这是一个关于正在发生的事情的理论。

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

代码语言:javascript
复制
import pkg.subpkg.two_longname as two

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

代码语言:javascript
复制
import pkg.subpkg.two_longname as two

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

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

代码语言:javascript
复制
#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
EN

Stack Overflow用户

发布于 2017-05-15 18:24:12

正如公认的答案所指出的,这是Python行为的一个问题。

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

Serhiy Storchaka的修复已合并,预计将在Python 3.7中使用

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24807434

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档