问题描述
我遇到了在 __init__.py
中导入并在包的模块中使用 import as
和绝对导入的问题.
I've run into a problem with having imports in __init__.py
and using import as
with absolute imports in modules of the package.
我的项目有一个子包,其中的 __init__.py
我使用 from import as
语句将其中一个类从模块提升"到子包级别.该模块使用绝对导入从该子包中导入其他模块.我收到此错误 AttributeError: 'module' object has no attribute 'subpkg'
.
My project has a subpackage and in it's __init__.py
I "lift" one of the classes from a module to the subpackage level with from import as
statement. The module imports other modules from that subpackage with absolute imports. I get this error 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'
解决方法
有一些改变使它起作用:
Workarounds
There are changes that make it work:
清空
pkg/subpkg/__init__.py
并直接从pkg.subpkg.one
导入.
我不认为这是一种选择,因为 AFAIK 将事物提升"到包级别是可以的.以下引用自一篇文章:
I don't consider this as an option because AFAIK "lifting" things to the package level is ok. Here is quote from an article:
在您的 __init__.py
中要做的一件常见事情是导入选定的类、函数等进入包级别,以便它们可以方便地从包中导入.
将one.py
中的import as
改为from import
:
from pkg.subpkg import two_longname
class One(two_longname.Two):
pass
这里唯一的缺点是我无法为模块创建短别名.我从@begueradj 的回答中得到了这个想法.
The only con here is that I can't create a short alias for module. I got that idea from @begueradj's answer.
也可以在 one.py
中使用相对导入来解决问题.但我认为这只是解决方法 #2 的一种变体.
It is also possible to use a relative import in one.py
to fix the problem. But I think it's just a variation of workaround #2.
有人能解释一下这里到底发生了什么吗?为什么
__init__.py
中的导入和import as
的使用组合会导致这些问题?
Can someone explain what is actually going on here? Why a combination of imports in
__init__.py
and usage ofimport as
leads to such problems?
有没有更好的解决方法?
Are there any better workarounds?
原始示例
这是我的原始示例.这不是很现实,但我不会删除它,所以@begueradj 的回答仍然有意义.
Original example
This is my original example. It's not very realistic but I'm not deleting it so @begueradj's answer still makes sense.
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 中有这个:
Initially I had this in one.py:
import pkg.subpkg.two as two
ONE = two.TWO
在这种情况下,我在导入时出错(就像在我的原始项目中使用 import as
一样).
In that case I get error on import (just like in my original project which uses import as
too).
推荐答案
你错误地假设一个别名不能使用 from ... import
,如 from ... import ... as
自 Python 2.0 以来一直存在.import ... as
是一种晦涩的语法,很少有人知道,但您在代码中偶然使用了它.
You incorrectly assume that one cannot have an alias with from ... import
, as from ... import ... as
has been there since Python 2.0. The import ... as
is the obscure syntax that not many know about, but which you use by accident in your code.
PEP 0221 声称以下两个是有效"的一样:
PEP 0221 claims that the following 2 are "effectively" the same:
将 foo.bar.bazaar 导入为 baz
from foo.bar import bazaar as baz
该声明在 Python 版本直到(包括 3.6.x) 中并不完全正确,正如您遇到的极端情况所证明的那样,即如果 sys.modules 但尚未初始化.
import ... as
要求模块 foo.bar
作为属性 bar
注入到 foo
命名空间中,除了在 sys.modules
中,而 from ... import ... as
在 foo.bar
中查找 >sys.modules
.
The statement is not quite true in Python versions up to and including 3.6.x as evidenced by the corner case you met, namely if the required modules already exist in sys.modules
but are yet uninitialized. The import ... as
requires that the module foo.bar
is injected in foo
namespace as the attribute bar
, in addition to being in sys.modules
, whereas the from ... import ... as
looks for foo.bar
in sys.modules
.
(还要注意 import foo.bar
仅确保模块 foo.bar
在 sys.modules
中并且可以作为 foo.bar
,但可能尚未完全初始化.)
(Do note also that import foo.bar
only ensures that the module foo.bar
is in sys.modules
and accessible as foo.bar
, but might not be fully initialized yet.)
如下更改代码对我有用:
Changing the code as follows did the trick for me:
# import pkg.subpkg.two_longname as two
from pkg.subpkg import two_longname as two
并且代码在 Python 2 和 Python 3 上都能完美运行.
And code runs perfectly on both Python 2 and Python 3.
此外,在 one.py
中,出于同样的原因,您不能执行 from pkg import subpkg
.
Also, in one.py
you cannot do from pkg import subpkg
, for the same reason.
为了进一步演示这个错误,修复你的 one.py
如上所述,并在 tst.py
中添加以下代码:
To demonstrate this bug further, fix your one.py
as above, and add the following code in 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
的 pkg.subpkg
并在那里找到它,而 import ... as
为 pkg
查询 sys.modules
并尝试在pkg
模块.因为我们刚刚删除了那个属性,最后一行失败了 AttributeError: 'module' object has no attribute 'subpkg'
.
Only the last line crashes, because from ... import
consults the sys.modules
for pkg.subpkg
and finds it there, whereas import ... as
consults sys.modules
for pkg
and tries to find subpkg
as an attribute in the pkg
module. As we just had deleted that attribute, the last line fails with AttributeError: 'module' object has no attribute 'subpkg'
.
由于 import foo.bar as baz
语法有点晦涩并且增加了更多的极端情况,而且我很少看到它被使用,我建议完全避免它并支持 from .. import ... as
.
As the import foo.bar as baz
syntax is a bit obscure and adds more corner cases, and I have rarely if ever seen it being used, I would recommend avoiding it completely and favouring from .. import ... as
.
这篇关于在 __init__.py 和 `import as` 语句中导入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!