我正在使用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?或者我是因为一个我不知道的完全不同的原因而得到这个错误的?
答案 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 eggs
与from 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或更改导入。