Python子子模块:“import as”vs“import”

时间:2017-10-05 18:54:25

标签: python python-2.7 python-import attributeerror

我正在使用Python 2.7。

这是我的文件夹结构。 (temp已添加到此系统路径中。)

temp
|
|--main.py
|
|--sub
   |
   |--__init__.py
   |
   |--sub2
      |
      |--__init__.py
      |
      |--square.py

文件内容如下所示。

main.py:

import sub.sub2 as sub2
sub2.run()
sub/__init__.py: empty
sub/sub2/__init__.py:

import sub.sub2.square as square
def run():
    square.square_it(3)
sub/sub2/square.py:

def square_it(x): return x**2

当我执行main.py时,收到以下错误(忽略行号):

Traceback (most recent call last):
  File "main.py", line 3, in <module>
    import sub.sub2 as sub2
  File "/home/gimlisonofgloin1/temp/sub/sub2/__init__.py", line 3, in <module>
    import sub.sub2.square as square
AttributeError: 'module' object has no attribute 'sub2'

我可以通过将发生错误的语句更改为任何这些语句来解决这个问题(在最后三个列出的解决方案中,我必须适当地更改函数调用):

  • from sub.sub2 import square as square;
  • from sub.sub2.square import square_it;
  • from .square import square_it(正如用户NeErAj KuMaR的答案所指出的那样);或
  • import sub.sub2.square

我的问题是:为什么原始代码行会产生错误,即使它在语义上等同于工作(“固定”)代码行(特别是列出的第1和第4个解决方案)?

在尝试回答这个问题时,我偶然发现了Python 2.0 Reference Manual中的这段文字:

  

为避免混淆,您无法将子模块导入为“不同的本地名称”。所以'导入模块为m'是合法的,但'import module.submod as s'不是。后者应该写成'从模块导入submod as s',见下文。

这与我收到的错误一致。然而,Python 2.7 Reference Manual中的任何地方都没有这个(看似很重要的)小模糊。 Python 2.0参考中的这一点是否仍然适用于Python 2.7?或者我是因为一个我不知道的完全不同的原因而得到这个错误的?

2 个答案:

答案 0 :(得分:0)

替换sub2 __init__.py代码的代码,如下所示

sub/sub2/__init__.py:

from .square import square_it
def run():
    square_it(3)

答案 1 :(得分:0)

我认为你偶然发现了一个鲜为人知的Python&#34; gotcha&#34;这似乎是fixed in Python 3.7 Alpha 1 thanks to Serhiy Storchaka。请查看此问题:http://stackoverflow.com/questions/41845671/import-as-in-python-3

Python Ideas中对此进行了讨论,但import foo.bar as eggsfrom foo import bar as eggs不同。它们会生成不一致的字节码。

  

根本原因是导入周期。我和dis一起玩了,发现了以下内容(我怀疑其他人已经发现了这个但我最初很难跟上这个帖子):

>>> dis.dis('import a.b')
1         0 LOAD_CONST               0 (0)
          2 LOAD_CONST               1 (None)
          4 IMPORT_NAME              0 (a.b)
          6 STORE_NAME               1 (a)
          8 LOAD_CONST               1 (None)
         10 RETURN_VALUE
>>>
  

相比
>>> dis.dis('import a.b as c')
1         0 LOAD_CONST               0 (0)
          2 LOAD_CONST               1 (None)
          4 IMPORT_NAME              0 (a.b)
          6 LOAD_ATTR                1 (b)      <-- error here
          8 STORE_NAME               2 (c)
         10 LOAD_CONST               1 (None)
         12 RETURN_VALUE
>>>
  

这表明&#34;导入a.b&#34;的实施和&#34;将a.b导入为c&#34;是不同的。前者调用 import (&#39; a.b&#39;,...),它返回模块&#39; a&#39;并将其存储在变量&#39; a&#39;中。在OP的情况下,由于导入周期,当sys.modules [&#39; a.b&#39;]存在时,模块&#39; a&#39;还没有属性&#39; b&#39;。这就是后一个例子中LOAD_ATTR操作码失败的原因。

>>> dis("import sys.path as path")
1         0 LOAD_CONST               0 (0)
          3 LOAD_CONST               1 (None)
          6 IMPORT_NAME              0 (sys.path)
          9 LOAD_ATTR                1 (path)
         12 STORE_NAME               1 (path)
         15 LOAD_CONST               1 (None)
         18 RETURN_VALUE
  

对于&#34;将sys.path导入为路径&#34;,给定的模块名称为&#34; sys.path&#34;,以及&#34; from list&#34;堆栈上的条目是None。这在它到达LOAD_ATTR行之前就失败了,因为&#34; sys.path&#34;不是一个可导入的模块。因此,将LOAD_ATTR更改为IMPORT_FROM对其行为没有影响。

>>> dis("from sys import path")
1         0 LOAD_CONST               0 (0)
          3 LOAD_CONST               1 (('path',))
          6 IMPORT_NAME              0 (sys)
          9 IMPORT_FROM              1 (path)
         12 STORE_NAME               1 (path)
         15 POP_TOP
         16 LOAD_CONST               2 (None)
         19 RETURN_VALUE
  

对于&#34;来自sys导入路径&#34;,给定的模块名称是&#34; sys&#34;,&#34;来自列表&#34;堆栈上的条目是一个包含字符串&#34; path&#34;的元组。这是有效的,因为&#34; sys&#34;是可导入的它有一个&#34;路径&#34;属性。

我猜您必须等待3.7或更改导入。