__init__.py 中的导入和“import as”语句
- 2025-03-21 09:06:00
- admin 原创
- 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/初始化.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'
解决方法
有一些变化可以使其发挥作用:
清空
pkg/subpkg/__init__.py
并直接从 导入pkg.subpkg.one
。
我不认为这是一个选项,因为据我所知,“提升”东西到包级别是可以的。以下是一篇文章的引文:
您要做的一件常见事情
__init__.py
是将选定的类、函数等导入到包级别,以便可以方便地从包中导入它们。
更改
import as
为:from import
one.py
from pkg.subpkg import two_longname
class One(two_longname.Two):
pass
这里唯一的缺点是我无法为模块创建一个简短的别名。这个想法是从@begueradj 的回答中得到的。
也可以使用相对导入来one.py
修复此问题。但我认为这只是解决方法 #2 的变体。
问题
有人能解释一下这里到底发生了什么吗?为什么导入
__init__.py
和使用的组合import as
会导致这样的问题?还有其他更好的解决方法吗?
原始示例
这是我原来的例子。它不太现实,但我不会删除它,所以@begueradj 的回答仍然有意义。
pkg/ init.py是**空的。
pkg/subpkg/初始化.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
)。
解决方案 1:
您错误地认为不能使用 别名from ... import
,因为from ... import ... as
Python 2.0 之后就有了 别名。 这import ... as
是一种晦涩难懂的语法,很多人都不知道,但您在代码中却不小心用到了它。
PEP 0221声称以下 2 点“实际上”是相同的:
import foo.bar.bazaar as baz
from foo.bar import bazaar as baz
您遇到的极端情况表明,在Python 3.6.x 及以下版本中,该陈述并不完全正确,即如果所需模块已存在于 中sys.modules
但尚未初始化。除了位于 中之外,import ... as
还要求将模块作为属性foo.bar
注入到 命名空间 中,而 则在 中查找。foo
`barsys.modules
from ... import ... asfoo.bar
sys.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
并且代码在 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 ... import
查询sys.modules
forpkg.subpkg
并在那里找到它,而import ... as
查询sys.modules
forpkg
并尝试在模块subpkg
中找到属性pkg
。由于我们刚刚删除了该属性,因此最后一行失败AttributeError: 'module' object has no attribute 'subpkg'
。
由于import foo.bar as baz
语法有点晦涩并增加了更多极端情况,而且我很少见到它被使用,因此我建议完全避免使用它并倾向于使用它from .. import ... as
。
解决方案 2:
正如接受的答案所述,这是 Python 行为的一个问题。
我已经提交了一个错误:http://bugs.python.org/issue30024
Serhiy Storchaka 的修复已合并并预计在 Python 3.7 中发布
解决方案 3:
以下是关于正在发生之事的一个理论。
当您使用as
保留字时,例如:
import pkg.subpkg.two_longname as two
Python 必须完全初始化并解决与有关的所有依赖关系pkg.subpkg
。但有一个问题,要完全加载,subpkg
您还需要完全加载one.py
,对吗?同时two_longname.py
使用as
关键字导入...您能在这里看到递归吗?这就是为什么在执行时:
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
是例外之一。我不知道是否还有其他例外,但了解它们会很有趣。
解决方案 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
希望这有帮助。
扫码咨询,免费领取项目管理大礼包!