我遇到了在__init__.py
中使用imports以及在包的模块中使用import as
和绝对imports的问题。
我的项目有一个子包,在它的__init__.py
中,我使用from import as
语句将其中一个类从模块“提升”到子包级别。该模块使用绝对导入从该子包导入其他模块。我得到了这个错误AttributeError: 'module' object has no attribute 'subpkg'
。
示例
Structure
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'
变通方法
有一些变化可以让它工作:
pkg/subpkg/__init__.py
并直接从pkg.subpkg.one
导入。我不认为这是一种选择,因为AFAIK将东西“提升”到包级别是可以的。以下是an article的引述:
在__init__.py
中通常要做的一件事是将选定的类、函数等导入到包级别,以便可以方便地从包中导入它们。
one.py
中将import as
更改为from import
:从pkg.subpkg导入two_longname第一类(two_longname.Two):pass
这里唯一的缺点是我不能为模块创建一个简短的别名。我是从@begueradj的回答中得到这个想法的。
也可以在one.py
中使用相对导入来解决此问题。但我认为这只是2号变通方法的一种变体。
问题
__init__.py
中的导入和import as
的使用相结合会导致这样的问题?还有更好的workarounds?吗?
原始示例
这是我最初的例子。这不是很现实,但我不会删除它,所以@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
)。
发布于 2014-07-26 16:07:09
您错误地认为不能使用from ... import
作为别名,因为from ... import ... as
从Python2.0开始就存在了。import ... as
是一种晦涩难懂的语法,很多人都不知道,但您在代码中意外地使用了它。
PEP 0221声称以下两个是“有效”相同的:
import foo.bar.bazaar as baz
from foo.bar import bazaar as baz
这一说法在3.6.x之前的Python版本中并不完全正确,正如您遇到的角落案例所证明的那样,即如果所需的模块已经存在于sys.modules
中,但尚未初始化。import ... as
要求模块foo.bar
在foo
名称空间中作为属性bar
注入,而from ... import ... as
则在sys.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
代码可以在Python2和Python3上完美运行。
此外,由于同样的原因,在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
中查询pkg.subpkg
并在那里找到它,而import ... as
在sys.modules
中查询pkg
并尝试将subpkg
作为pkg
模块中的一个属性。因为我们刚刚删除了该属性,所以最后一行将失败,并显示AttributeError: 'module' object has no attribute 'subpkg'
。
由于import foo.bar as baz
语法有点晦涩难懂,并且添加了更多的角例,而且我很少看到它被使用,所以我建议完全避免使用它,并倾向于使用from .. import ... as
。
发布于 2014-07-23 21:50:56
这是一个关于正在发生的事情的理论。
当您使用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
关键字是一个例外。我不知道是否还有其他人,但知道他们会很有趣。
发布于 2017-05-15 18:24:12
正如公认的答案所指出的,这是Python行为的一个问题。
我提交了一个bug:http://bugs.python.org/issue30024
Serhiy Storchaka的修复已合并,预计将在Python 3.7中使用
https://stackoverflow.com/questions/24807434
复制相似问题