import as在python_关于python:导入__init__.py和`import as`语句

博客探讨了在Python包结构中使用`__init__.py`进行导入以及`import as`时遇到的问题。作者详细描述了一个导致AttributeError的示例,并提供了几个解决方案,包括清空`__init__.py`、使用相对导入和绝对导入的不同效果。文章讨论了Python导入机制的细节,解释了为何特定导入方式会导致问题,并提出避免使用`import as`的建议。
摘要由CSDN通过智能技术生成

我在__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使类,函数等在程序包级别可用绝对是可以的。

@是的,还可以。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值