我在__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
from pkg.subpkg import One
File"/home/and/dev/test/python/imptest2/pkg/subpkg/__init__.py", line 1, in
from pkg.subpkg.one import One
File"/home/and/dev/test/python/imptest2/pkg/subpkg/one.py", line 1, in
import pkg.subpkg.two_longname as two
AttributeError: 'module' object has no attribute 'subpkg'
解决方法
有一些使其生效的更改:
清空pkg/subpkg/__init__.py并直接从pkg.subpkg.one导入。
我不认为这是一个选择,因为AFAIK可以将其"提升"到包装级别。这是一篇文章的引文:
One common thing to do in your __init__.py is to import selected
Classes, functions, etc into the package level so they can be
conveniently imported from the package.
在one.py中将import as更改为from import:
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 / __ 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
from pkg.subpkg import ONE
File"/home/and/dev/test/python/imptest/pkg/subpkg/__init__.py", line 2, in
from pkg.subpkg.one import ONE
File"/home/and/dev/test/python/imptest/pkg/subpkg/one.py", line 6, in
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.SOMETHING或from pkg.subpkg import SOMETHING时,都会遇到相同的错误。 我重新编辑了答案:我只更改了程序的结构,主要是tst.py在pkg之外(实际上,自从我第一次在下面发布答案以来,我就以此方式运行它)。 如果您想将tst.py保留在pkg中,请从所有呼叫(例如import pkg.subpkg.SOMETHING和from pkg.subpkg import SOMETHING)中删除pgk
tst.py在包装内,就是这种情况。"删除所有呼叫..."是什么意思? 相对进口? 我知道这将适用于相对进口。 问题是为什么它不能按原样工作。
我的意思是您需要运行import subpkg.SOMETHING而不是import pkg.subpkg.SOMETHING和from subpkg import SOMETHING而不是from pkg.subpkg import SOMETHING。 为什么呢 好吧:您不能使用无效路径运行程序。 就这么简单。
您错误地认为一个人不能使用from ... import作为别名,因为from ... import ... as自从Python 2.0开始就存在。 import ... as是很多人都不知道的晦涩语法,但是您在代码中偶然使用了它。
PEP 0221声称以下两个"有效"相同:
import foo.bar.bazaar as baz
from foo.bar import bazaar as baz
正如您遇到的特殊情况所证明的,即在sys.modules中已经存在但尚未初始化的模块中,该语句在3.6.x及以下版本的Python版本中并不完全正确。 import ... as除了在sys.modules中之外,还要求模块foo.bar作为属性bar注入到foo命名空间中,而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
而且代码可以在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为pkg.subpkg咨询sys.modules并在此找到它,而import ... as为pkg咨询sys.modules并尝试在subpkg >模块。由于我们刚刚删除了该属性,因此最后一行以AttributeError: 'module' object has no attribute 'subpkg'失败。
由于import foo.bar as baz语法有点晦涩难懂,并且添加了更多的极端情况,而且我很少(甚至从未见过)被使用过,因此建议您完全避免使用它,而建议使用from .. import ... as。
现在,此问题已在Python 3.7中修复,请参见问题#23203和#30024。
可能值得注意的是,在python <3.7中,import pkg.subpkg.two_longname实际上有效,而import pkg.subpkg.two_longname as two无效。 因此问题不是由import ...与from ... import ...引起的; 在python 3.7中都可以使用; 问题30024很好地涵盖了这一点;
正如公认的答案所言,这是Python行为的问题。
我提交了一个错误:http://bugs.python.org/issue30024
Serhiy Storchaka的修复已合并并在Python 3.7中预期
这是关于正在发生的事情的理论。
例如,当您使用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关键字是例外之一。我不知道是否还有其他人,但是了解他们会很有趣。
关于调用模块方式的项目结构必须如下所示:
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使类,函数等在程序包级别可用绝对是可以的。
@是的,还可以。