Python子子模块:“导入为”与“导入”

上午

我正在使用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

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

在尝试回答这个问题时,我偶然发现了《Python 2.0参考手册》中的这段文字

为避免混淆,您不能将子模块“作为”导入其他本地名称。因此,'import module as m'是合法的,但'import module.submod as s'不是合法的。后者应写为“ from module import submod as s”,请参见下文。

这与我收到的错误一致。但是,《Python 2.7参考手册》中的任何地方都没有这个(看似重要的)模糊效果从Python 2.0参考中获得的少量内容仍然适用于Python 2.7吗?还是因为我不知道的完全不同的原因而收到此错误?

保罗·斯卡丁

我认为您偶然发现了一个鲜为人知的Python“陷阱”,这要归功于Serhiy Storchaka在Python 3.7 Alpha 1中得到了解决检查以下问题: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
>>>

这说明“ import ab”和“将ab as c导入”的实现是不同的。前者调用import('a.b',...),后者返回模块'a'并将其存储在变量'a'中。在OP的情况下,由于导入周期的原因,虽然存在sys.modules ['a.b'],但模块'a'尚未具有属性'b'。这就是在后一个示例中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

对于“将sys.path导入为路径”,给定的模块名称为“ sys.path”,并且堆栈上的“ from list”条目为None。LOAD_ATTR由于“ sys.path”不是可导入的模块,因此它甚至无法运行。因此,将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

对于“ from sys import path”,给定的模块名称是“ sys”,堆栈上的“ from list”条目是一个包含字符串“ path”的元组。这是可行的,因为“ sys”是可导入的并且具有“ path”属性。

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

TOP 榜单

热门标签

归档